/*
Copyright (C) 1996-1997 GX Media, Inc.
Copyright (C) 2010 Ronie Salgado

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/

#include "stdafx.h"
#include "QViewPanel.h"
#include "QDraw.h"
#include "QView.h"
#include "QMainFrame.h"

// Extra hint flags.
#define DUQV_DISPLAYDRAGBOX		0x1000
#define DUQV_UPDATEMANIPSTAT	0x2000
#define DUQV_INITPLANEDRAG		0x4000
#define DUQV_UPDATEGRIDS		0x8000

// QView

Vector3d QView::opCenterVec;
Vector3d QView::oldOpCenterVec;
Vector3d QView::opCornerVec;
unsigned int QView::modifyBrushMode;
const GPolygon *QView::pManipFace;
const Edge3d *QView::pManipEdge;
int QView::manipVertex;
bool QView::dispManipDots = false;
bool QView::drawScaleBoundBox = false;
bool QView::updateSclBoundBox = false;

LConfig *QView::cfg = NULL;

bool QView::invLMouse = false;
bool QView::invRMouse = false;
unsigned int QView::walkSpeed = 16;
unsigned int QView::sensitivity = 10;

unsigned int QView::keyForward = 'W';
unsigned int QView::keyBackward = 'S';
unsigned int QView::keyLeft = 'A';
unsigned int QView::keyRight = 'D';
unsigned int QView::keyUp = 'Q';
unsigned int QView::keyDown = 'E';

unsigned int QView::snapAlignment = 0;
unsigned int QView::editConstraint = 0;
unsigned int QView::dragThreshold = 2;

wxString QView::draw2dWire = wxT("WX");
wxString QView::draw3dWire = wxT("OpenGL");
wxString QView::draw3dSolid = wxT("OpenGL");
wxString QView::draw3dTex = wxT("OpenGL");

IMPLEMENT_DYNAMIC_CLASS(QView, wxView);

BEGIN_EVENT_TABLE(QView, wxView)
	EVT_MOTION(QView::OnMouseMove)
	EVT_MOUSEWHEEL(QView::OnMouseWheel)
	EVT_LEFT_DOWN(QView::OnLButtonDown)
	EVT_LEFT_UP(QView::OnLButtonUp)
	EVT_MIDDLE_DOWN(QView::OnMButtonDown)
	EVT_MIDDLE_UP(QView::OnMButtonUp)
	EVT_RIGHT_DOWN(QView::OnRButtonDown)
	EVT_RIGHT_UP(QView::OnRButtonUp)
	EVT_KEY_UP(QView::OnKeyUp)
	EVT_KEY_DOWN(QView::OnKeyDown)
	EVT_CHAR(QView::OnChar)
	//EVT_CONTEXT_MENU(QView::OnContextMenu)
	EVT_MENU(wxID_PASTE, QView::OnEditPaste)
	EVT_UPDATE_UI(wxID_PASTE, QView::OnEditPasteUI)
	EVT_MENU(wxID_DUPLICATE, QView::OnEditDuplicate)
	EVT_MENU(XRCID("menuIncreaseGrid"), QView::OnEditIncrGridSize)
	EVT_MENU(XRCID("menuDecreaseGrid"), QView::OnEditDecrGridSize)
	EVT_UPDATE_UI(wxID_DUPLICATE, QView::OnEditDuplicateUI)
	EVT_MENU(XRCID("menuAlignSetMark"), QView::OnEditAlignSet)
	EVT_MENU(XRCID("menuAlignCenter"), QView::OnEditAlignSelection)
	EVT_MENU(XRCID("menuAlignTop"), QView::OnEditAlignSelection)
	EVT_MENU(XRCID("menuAlignBottom"), QView::OnEditAlignSelection)
	EVT_MENU(XRCID("menuAlignLeft"), QView::OnEditAlignSelection)
	EVT_MENU(XRCID("menuAlignRight"), QView::OnEditAlignSelection)
	EVT_UPDATE_UI(XRCID("menuAlignSetMark"), QView::OnEditAlignUI)
	EVT_UPDATE_UI(XRCID("menuAlignCenter"), QView::OnEditAlignUI)
	EVT_UPDATE_UI(XRCID("menuAlignTop"), QView::OnEditAlignUI)
	EVT_UPDATE_UI(XRCID("menuAlignBottom"), QView::OnEditAlignUI)
	EVT_UPDATE_UI(XRCID("menuAlignLeft"), QView::OnEditAlignUI)
	EVT_UPDATE_UI(XRCID("menuAlignRight"), QView::OnEditAlignUI)
	EVT_MENU(XRCID("renderWireFrame"), QView::OnRenderWireFrame)
	EVT_UPDATE_UI(XRCID("renderWireFrame"), QView::OnRenderWireFrameUI)
	EVT_MENU(XRCID("renderSolid"), QView::OnRenderSolid)
	EVT_UPDATE_UI(XRCID("renderSolid"), QView::OnRenderSolidUI)
	EVT_MENU(XRCID("renderTextured"), QView::OnRenderTextured)
	EVT_UPDATE_UI(XRCID("renderTextured"), QView::OnRenderTexturedUI)
	EVT_MENU(XRCID("renderShaded"), QView::OnRenderShaded)
	EVT_UPDATE_UI(XRCID("renderShaded"), QView::OnRenderShadedUI)
	EVT_TIMER(QID_VIEW_TIMER_UPDATEDRAGSEL, QView::OnUpdateDragSelect)
	EVT_TIMER(QID_VIEW_TIMER_SCROLLVIEW, QView::OnQViewScrollView)
	EVT_TIMER(QID_VIEW_TIMER_RENDERQVIEWS, QView::OnTimerUpdateQViews)
	EVT_TIMER(QID_VIEW_TIMER_RENDERLOCKEDQVIEWS, QView::OnTimerUpdateLockedQViews)
	EVT_TIMER(QID_VIEW_TIMER_UPDATE, QView::OnEndViewMove)
END_EVENT_TABLE()

void QView::Init()
{
	cfg = new LConfig(wxT("QView"));

	cfg->RegisterVar(wxT("InvLMouse"), &invLMouse, LVAR_INT);
	cfg->RegisterVar(wxT("InvRMouse"), &invRMouse, LVAR_INT);
	cfg->RegisterVar(wxT("WalkSpeed"), &walkSpeed, LVAR_INT);
	cfg->RegisterVar(wxT("Sensitivity"), &sensitivity, LVAR_INT);
	cfg->RegisterVar(wxT("DragThreshold"), &dragThreshold, LVAR_INT);

	cfg->RegisterVar(wxT("KeyForward"), &keyForward, LVAR_INT);
	cfg->RegisterVar(wxT("KeyBackward"), &keyBackward, LVAR_INT);
	cfg->RegisterVar(wxT("KeyLeft"), &keyLeft, LVAR_INT);
	cfg->RegisterVar(wxT("KeyRight"), &keyRight, LVAR_INT);
	cfg->RegisterVar(wxT("KeyUp"), &keyUp, LVAR_INT);
	cfg->RegisterVar(wxT("KeyDown"), &keyDown, LVAR_INT);

	// cfg->RegisterVar(wxT("SnapAlignment"), &snapAlignment, LVAR_INT);
	cfg->RegisterVar(wxT("EditConstraint"), &editConstraint, LVAR_INT);

	cfg->RegisterVar(wxT("Draw2DWire"), &draw2dWire, LVAR_WXSTR);
	cfg->RegisterVar(wxT("Draw3DWire"), &draw3dWire, LVAR_WXSTR);
	cfg->RegisterVar(wxT("Draw3DSolid"), &draw3dSolid, LVAR_WXSTR);
	cfg->RegisterVar(wxT("Draw3DTex"), &draw3dTex, LVAR_WXSTR);
}

void QView::Exit()
{
	cfg->SaveVars();
	delete cfg;
}

QView::QView()
{
	view = NULL;
	draw = NULL;
	parentFrame = NULL;
	viewFrame = NULL;
	viewType = VT_BACK;
	subOperationMode = OPM_NONE;

	zoomVal = 1.0f;
	width = 1;
	height = 1;
	orgX = 0;
	orgY = 0;

	bits = 32;

	mouseLClick = mouseMClick = mouseRClick = false;
	mouseLDrag = mouseMDrag = mouseRDrag = false;
	reverseSelect = false;
	selectPending = selectAddPending = false;
	dragSelect = dragAddSelect = false;
	operatePending = dragOperate = dragOpCenter = false;
	scrollingView = 0;
	useBack = false;
	planeDefined = false;
	snapCorner = false;
	dragPlanePt = 0;

	qviewsBefore = NULL;

	scissors = wxXmlResource::Get()->LoadBitmap(wxT("imageScissors"));
}

QView::~QView()
{
	delete draw;
}

wxWindow *QView::GetViewFrame()
{
	return viewFrame;
}

const wxWindow *QView::GetViewFrame() const
{
	return viewFrame;
}

wxWindow *QView::GetParentFrame()
{
	return parentFrame;
}

void QView::SetParentFrame(wxWindow *parent)
{
	parentFrame = parent;
}

void QView::InitView(int id, Object *viewScope,
				  Selector *selector, const Vector3d &posVec)
{
	ASSERT(id >= VT_TOP && id <= VT_SIDE);
	viewType = ViewType(id);
	renderType = RT_WIREFRAME;

	CreateTimers();

	view = new View(NULL, *viewScope, *selector);
	CreateQDraw();

	Vector3d vec(posVec);
	SphrVector orientVec;
	bool perspective;

	switch(viewType)
	{
	case VT_TOP:
	{
		perspective = false;
		orientVec.NewVector(0.0f, DEG2RAD(-90.0f), 0.0f);
		zoomVal = 0.75f;
	}
		break;
	case VT_3D:
	{
		Vector3d fv(-100.0f, -100.0f, 100.0f);
		Vector3d tv(0.0f, 0.0f, 1.0f);

		perspective = true;
		vec.AddVector(fv);
		orientVec.NewVector(fv.MultVector(-1.0f), tv);

		zoomVal = 1.0f;
	}
		break;
	case VT_BACK:
	{
		perspective = false;
		orientVec.NewVector(0.0f, 0.0f , 0.0f);
		zoomVal = 0.75f;
	}
		break;
	case VT_SIDE:
	{
		perspective = false;
		orientVec.NewVector(DEG2RAD(-90.0f), 0.0f , 0.0f);

		zoomVal = 0.75f;
	}
		break;
	};

	// Need to use default configurable viewDepth insteal of 1000.0f.
	view->SetNewView(*viewScope, *selector, true, &vec,
					&orientVec, perspective, 1, 1000.0f);

	view->ShowCrossHair(true);
	view->SetCrossHairPos(Vector3d(0.0f, 0.0f, 0.0f));

	// Update the operating mode.
	OperationMode opMode = GetMainFrame()->GetOpMode();
	OnOpModeChanged(opMode, opMode);
}

void QView::CreateQDraw(void)
{
	wxString driver;
	int oldBits = bits;

	if(!GetDocument()->GetGame()->GetPalName().empty())
		bits = 8;
	else
		bits = 16;

	if(viewType == VT_3D)
	{
		if(renderType == RT_WIREFRAME)
		{
			driver = draw3dWire;
			bits = 8;
		}
		else if(renderType == RT_SOLID)
			driver = draw3dSolid;
		else if(renderType == RT_TEXTURED)
			driver = draw3dTex;
		else if(renderType == RT_SHADED)
			driver = draw3dTex;
	}
	else
	{
		driver = draw2dWire;
		bits = 8;
	}

	if(!draw || draw->GetDriver().CmpNoCase(driver) ||
		(bits != oldBits && !draw->SameBits()))
	{
		delete draw;
		draw = QDraw::New(driver, bits, GetViewFrame(), 2);

		ASSERT(draw);
		draw->SetZoomPtr(&zoomVal);
		draw->RenderMode(renderType);
		view->SetQDraw(draw);
	}

	if(draw->SameBits())
		draw->UpdateBits(bits);

	if(viewType == VT_3D)
		draw->ZBufInit();

	useBack = false;

	SetColors();
}

void QView::CreateTimers()
{
	timerUpdateDragSel.SetOwner(this, QID_VIEW_TIMER_UPDATEDRAGSEL);
	timerScrollView.SetOwner(this, QID_VIEW_TIMER_SCROLLVIEW);
	timerRenderViews.SetOwner(this, QID_VIEW_TIMER_RENDERQVIEWS);
	timerRenderLockedViews.SetOwner(this, QID_VIEW_TIMER_RENDERLOCKEDQVIEWS);
	timerUpdateViews.SetOwner(this, QID_VIEW_TIMER_UPDATE);
}

void QView::SetColors(void)
{
	draw->SetColor(VRC_VIEW, 0, 0, 0);
	draw->SetColor(VRC_NORMAL, 255, 255, 255);
	draw->SetColor(VRC_SELECT, 255, 0, 0);
	draw->SetColor(VRC_HOTSELECT, 255, 150, 150);
	draw->SetColor(VRC_ENTITY, 120, 150, 255);
	draw->SetColor(VRC_MANIPDOT, 255, 255, 0);
	draw->SetColor(VRC_FACESELECT, 255, 255, 0);
	draw->SetColor(VRC_CROSSHAIR, 24, 100, 244);
	draw->SetColor(VRC_CROSSHAIRNORTH, 4, 208, 24);
	draw->SetColor(VRC_OPCENTER, 32, 192, 255);
	draw->SetColor(VRC_LEAKGEOM, 255, 128, 0);

	if(renderType == RT_WIREFRAME)
	{
		draw->RealizePal();
	}
	else
	{
		wxString palName = GetDocument()->GetGame()->GetPalName();
		if(!palName.empty())
			draw->LoadPal(palName);
	}
}

void QView::SelColors(int vrc, int red, int green, int blue)
{
	draw->SetColor(VRC_VIEW, 0, 0, 0);
	draw->SetColor(VRC_NORMAL, 255, 255, 255);
	draw->SetColor(VRC_SELECT, 255, 0, 0);
	draw->SetColor(VRC_HOTSELECT, 255, 150, 150);
	draw->SetColor(VRC_ENTITY, 120, 150, 255);
	draw->SetColor(VRC_MANIPDOT, 255, 255, 0);
	draw->SetColor(VRC_FACESELECT, 255, 255, 0);
	draw->SetColor(VRC_CROSSHAIR, 24, 100, 244);
	draw->SetColor(VRC_CROSSHAIRNORTH, 4, 208, 24);
	draw->SetColor(VRC_OPCENTER, 32, 192, 255);
	draw->SetColor(VRC_LEAKGEOM, 255, 128, 0);
	draw->SetColor(vrc, red, green, blue);

	if(renderType == RT_WIREFRAME)
	{
		draw->RealizePal();
	}
	else
	{
		wxString palName = GetDocument()->GetGame()->GetPalName();
		if(!palName.empty())
			draw->LoadPal(palName);
	}
}

void QView::SuspendRenderLib(bool suspend)
{
	if(suspend)
		draw->Suspend();
	else
	{
		draw->Resume();
		OnUpdate(NULL, NULL);
	}
}

// Interface to undo / redo module
void QView::GetViewState(Vector3d &posVec, SphrVector &oriVec,
				  float &zoomRate, ViewType *viewType) const
{
	view->GetPosition(posVec);
	view->GetOrientation(oriVec);
	zoomRate = zoomVal;
	if(viewType != NULL)
		*viewType = this->viewType;
}

void QView::SetViewState(const Vector3d &posVec, const SphrVector &oriVec,
				  float zoomRate, bool updateView)
{
	view->SetPosition(posVec);
	view->SetOrientation(oriVec);

	view->SetCrossHairPos(GetMainFrame()->GetEditFocusPos());

	if(zoomVal != zoomRate)
	{
		zoomVal = zoomRate;
		OnZoomChanged();
	}

	// Update the view.
	if(updateView)
		OnUpdate(NULL, NULL);
}

void QView::RenderSelectedFace(bool visible)
{
	view->DisplayFaceSelect(visible);
}

// View creation/deletion.
bool QView::OnCreate(wxDocument* doc, long flags)
{
	ASSERT(parentFrame);

	// Create the child frame for this view.
	QViewPanel *frame =
			new QViewPanel(this, parentFrame, wxID_ANY, wxT("QView"));
	viewFrame = frame;

	// Activate the view.
	Activate(true);

	return true;
}

bool QView::OnClose(bool deleteWindow)
{
	if (!GetDocument()->Close())
	    return false;

	// Deactivate this view.
	Activate(false);

	// Delete the draw driver.
	delete draw;
	draw = NULL;

	// Delete the window, if needed.
	if(deleteWindow)
	{
		wxWindow *frame = GetFrame();
		if(frame)
		{
			frame->Close(true);
			frame->Destroy();
			frame = NULL;
		}
	}
	return true;
}

// Operation position.
const Vector3d &QView::GetOperateCenterPos(void)
{
	return opCenterVec;
}

void QView::SetOperateCenterPos(const Vector3d &opCenterPos)
{
	opCenterVec = opCenterPos;
}

// General QView Maintaince
void QView::OnSize(const wxSize &newSize)
{
	// Remember the size.
	width = newSize.GetWidth();
	height = newSize.GetHeight();

	// Remember the center.
	orgX = width/2;
	orgY = height/2;

	if(view)
	{
		ASSERT(draw);

		if(width > 0 && height > 0)
			draw->Size(width, height);

		view->SetWindowWidth((int) ((float)Max(width,height) / (2.0f*zoomVal)));
		useBack = false;
		OnUpdate(NULL, NULL);
	}
}

void QView::OnZoomChanged(void)
{
	ASSERT(view != NULL);

	// Need to update the clip area for views.
	wxRect rect = GetViewFrame()->GetClientRect();;
	float width = Max(rect.GetRight(), rect.GetBottom()) / (2.0f * zoomVal);
	view->SetWindowWidth((int) width);
}

// QView render stuffs.
static int paintCount = 0;

void QView::OnDraw(wxDC *dc)
{
	draw->Paint(dc);
	paintCount++;
}

void QView::OnUpdate(wxView* sender, wxObject* hint)
{
	// First check the hint.
	QDocHint *qhint = static_cast<QDocHint*> (hint);
	int flags = 0;
	if(qhint)
		flags = qhint->flags;

	if(flags & DUAV_NOQVIEWS)
		return;

	// Update the size info.
	if(flags & DUAV_OBJSSEL)
		UpdateSizeInfo();

	if(flags & DUAV_SCOPECHANGED)
	{
		OnScopeChanged(qhint->scope);
		return;
	}

	if(flags & DUQV_UPDATEMANIPSTAT)
		UpdateManipDispStat();

	// Check that my document is the desktop one.
	if(!GetDocument() || GetDocument() != GetMainFrame()->GetDeskTopDocument())
		return;

	Vector3d viewPos = view->GetPosition();
	SphrVector orient = view->GetOrientation();
	Object *viewObj = view->GetViewObjPtr();

	bool drawFront = true;
	bool objChanged = (flags & (DUAV_SUPDATEALL | DUQV_UPDATEGRIDS) ? true : false);

	if (renderType == RT_WIREFRAME && draw->GetNumBufs() == 2)
	{
		if (viewPos == oldViewPos && orient == oldOrient &&
			zoomVal == oldZoomVal && viewObj == oldViewObj && !objChanged) {
			if(!useBack) {
				useBack = true;
				draw->UseBuf(1);
			}
			else
				drawFront = false;
		}
		else
			useBack = false;
	}
	else
	{
		useBack = false;
		draw->UseBuf(0);
	}

	if(drawFront)
	{
		draw->Begin();
		draw->Clear();

		if(renderType == RT_TEXTURED ||
		   renderType == RT_SHADED)
		{
			draw->ZBufClear();
			draw->ZBufTextured(true);
			view->RenderSolid(true);
			//draw->ZBufRender();
		}
		else if(renderType == RT_SOLID)
		{
			draw->ZBufClear();
			draw->ZBufTextured(false);
			view->RenderSolid(false);
			//draw->ZBufRender();
		}
		else
		{
			// Draw grids in 2d views.
			if(viewType != VT_3D)
			{
				Matrix44 trans;

				trans.SetIdentity();
				view->CalInvTransSpaceMatrix(trans);

				Object *obj = view->GetViewObjPtr();
				ASSERT(obj != NULL);
				while (obj->GetParentPtr() != NULL)
				{
					obj->CalInvTransSpaceMatrix(trans);
					obj = obj->GetParentPtr();
				}

				Vector3d origPos(0.0f, 0.0f, 0.0f);
				trans.Transform(origPos);
				draw->gridStep1 = GetDocument()->GetGridSnapSize();
				draw->gridStep2 = GetDocument()->GetGame()->GetGridSteps();
				draw->Grid(origPos.GetX(), origPos.GetZ());
			}

			view->RenderWireFrame(true, false, viewType == VT_3D);
		}
	}

	if(useBack)
	{
		draw->UseBuf(0);
		draw->CopyBuf(1);
	}

	if(renderType == RT_WIREFRAME)
	{
		draw->Begin();
		view->RenderWireFrame(false, true, false);
	}

	oldViewPos = viewPos;
	oldOrient = orient;
	oldZoomVal = zoomVal;
	oldViewObj = viewObj;

	DrawOpCenter();

	if(flags & DUQV_DISPLAYDRAGBOX)
		DrawDragBox();

	if(flags & (DUAV_OBJSSEL | DUAV_OBJSMODATTRIB))
		CalcScaleManipDots();

	if(drawScaleBoundBox && viewType != VT_3D)
		DrawScaleBoundBox();

	if(flags & DUQV_INITPLANEDRAG)
	{
		planeDefined = false;
		dragPlanePt = 0;
	}

	if(planeDefined)
		DrawOpPlane();

	if(GetFrame())
		GetFrame()->Refresh(false);

	if(planeDefined)
		DrawCutSide();
}

// Screen drawing stuff.
void QView::DrawOpCenter(void)
{
	// Figure out if we should render the op center.
	QooleDoc *document = GetDocument();
	Selector *selector = &(GetMainFrame()->GetSelector());
	if(!selector)
		return;

	OperationMode opMode = GetMainFrame()->GetOpMode();
	if (opMode != OPM_OBJECT_MULTITOOL && opMode != OPM_OBJECT_SELECT &&
		opMode != OPM_OBJECT_MOVE && opMode != OPM_OBJECT_ROTATE &&
		opMode != OPM_OBJECT_SCALE)
		return;

	if (selector->GetNumMSelectedObjects() == 0)
		return;

	if (renderType == RT_SOLID || renderType == RT_TEXTURED
		|| renderType == RT_SHADED)
		return;

	wxPoint opCenterPos;
	FindOpCenterPos(opCenterPos);

	// Draw the X mark at opcenterPos.
	draw->Color(VRC_OPCENTER);
	draw->Cross(opCenterPos.x, opCenterPos.y, 5);
}

void QView::DrawDragBox(void)
{
	// draw rect at coords dragBoxRect.
	draw->Color(VRC_OPCENTER);
	draw->Box(dragBoxRect.GetLeft(), dragBoxRect.GetTop(), dragBoxRect.GetRight(), dragBoxRect.GetBottom());
}

void QView::DrawScaleBoundBox(void)
{
	// Draw the scale bounding boxes.
	Selector *pSlctr = &GetMainFrame()->GetSelector();
	if (pSlctr->GetNumMSelectedObjects() == 0)
		return;

	wxPoint pt1, pt2;
	if (viewType != VT_3D)
	{
		LC2DC(scaleManipDots[0].GetX(), scaleManipDots[0].GetZ(), pt1);
		LC2DC(scaleManipDots[4].GetX(), scaleManipDots[4].GetZ(), pt2);
	}
	else
	{
		LC2DC(scaleManipDots[0], pt1);
		LC2DC(scaleManipDots[4], pt2);
	}

	draw->Color(View::GetColor(VRC_MANIPDOT));
	draw->StippleBox(pt1.x, pt1.y, pt2.x, pt2.y, 5, 5);

	for (int i = 0; i < 8; i++)
	{
		if (viewType != VT_3D)
			LC2DC(scaleManipDots[i].GetX(), scaleManipDots[i].GetZ(), pt1);
		else
			LC2DC(scaleManipDots[i], pt1);
		draw->Point(pt1.x, pt1.y, 5);
	}
}

void QView::CalcScaleManipDots(void)
{
	if (!drawScaleBoundBox)
		return;

	ASSERT(GetMainFrame()->GetOpMode() == OPM_OBJECT_SCALE ||
			GetMainFrame()->GetOpMode() == OPM_OBJECT_MULTITOOL);

	Selector *pSlctr = &GetMainFrame()->GetSelector();
	if (pSlctr->GetNumMSelectedObjects() == 0)
		return;

	QooleDoc *pDoc = GetDocument();
	Vector3d minVec, maxVec;
	pDoc->GetObjectsScaleBoundBox(pSlctr->GetMSelectedObjects(),
								  *view, minVec, maxVec);

	float x1, z1, x2, z2, xAve, yAve, zAve;
	x1 = minVec.GetX();
	z1 = minVec.GetZ();
	x2 = maxVec.GetX();
	z2 = maxVec.GetZ();
	xAve = (x1 + x2) / 2.0f;
	yAve = (minVec.GetY() + maxVec.GetY()) / 2.0f;
	zAve = (z1 + z2) / 2.0f;

	scaleManipDots[0].NewVector(x1,   yAve, z1);
	scaleManipDots[1].NewVector(x1,   yAve, zAve);
	scaleManipDots[2].NewVector(x1,   yAve, z2);
	scaleManipDots[3].NewVector(xAve, yAve, z2);
	scaleManipDots[4].NewVector(x2,   yAve, z2);
	scaleManipDots[5].NewVector(x2,   yAve, zAve);
	scaleManipDots[6].NewVector(x2,   yAve, z1);
	scaleManipDots[7].NewVector(xAve, yAve, z1);
}

void QView::DrawOpPlane(void)
{
	// Draw the operate plane (line) for PlaneCut && Mirror funcs.
	ASSERT (GetMainFrame()->GetOpMode() == OPM_PLANE_CLIP ||
			GetMainFrame()->GetOpMode() == OPM_MIRROR);

	wxPoint pt1, pt2;
	LC2DC(planeDragPt1.GetX(), planeDragPt1.GetZ(), pt1);
	LC2DC(planeDragPt2.GetX(), planeDragPt2.GetZ(), pt2);

	draw->Color(View::GetColor(VRC_MANIPDOT));

	// Draw the 2 dots.
	draw->Point(pt1.x, pt1.y, 5);
	draw->Point(pt2.x, pt2.y, 5);

	if (pt1 == pt2)  // Dont draw the line.
		return;

	// Calc the line's slope for render
	wxPoint slope = pt2 - pt1;
	wxPoint invSlope(-slope.x, slope.y);
	float length = (float) sqrt((float) slope.x * (float) slope.x +
								(float) slope.y * (float) slope.y);
	int mult = (int) (5000.0f / length);

	slope.x *= mult;
	slope.y *= mult;

	pt1 -= slope;
	pt2 += slope;

	// Draw the line.
	draw->Line(pt1.x, pt1.y, pt2.x, pt2.y);
}

void QView::DrawCutSide(void)
{
	// Draw the scissors bitmap to indicate the cuttin side.
	if (GetMainFrame()->GetOpMode() != OPM_PLANE_CLIP)
		return;

	wxPoint pt1, pt2;
	LC2DC(planeDragPt1.GetX(), planeDragPt1.GetZ(), pt1);
	LC2DC(planeDragPt2.GetX(), planeDragPt2.GetZ(), pt2);

	if (pt1 == pt2)
		return;

	// Calc the line's slope for render
	wxPoint slope = pt2 - pt1;
	float length = (float) sqrt((float) slope.x * (float) slope.x +
								(float) slope.y * (float) slope.y);
	float mult = 25.0f / length;
	wxPoint invSlope((int) (slope.y * mult), (int) (-slope.x * mult));

	if (clipSide == -1) // Left
		pt1 += invSlope;
	else if (clipSide == 1) // Right
		pt1 -= invSlope;
	else { // clipSide == 0, Middle
		pt1.x += ((int) (slope.x * mult));
		pt1.y += ((int) (slope.y * mult));
	}

	pt1.x -= scissors.GetWidth() / 2;
	pt1.y -= scissors.GetHeight() / 2;

	draw->Blit(pt1.x, pt1.y, scissors.GetWidth(), scissors.GetHeight(),
			scissors, 0, 0, wxCOPY, true);
}

// Adjust the views.
void QView::OnScopeChanged(Object *pOldScope)
{
	// Sanity.
	ASSERT(pOldScope == view->GetViewObjPtr());

	QooleDoc *pDoc = GetDocument();
	Object *pRootObj = pDoc->GetRootObjectPtr();
	ASSERT(pOldScope == pRootObj || pOldScope->IsMyAncestor(*pRootObj));

	Selector *pSlct = &GetMainFrame()->GetSelector();
	Object *pNewScope = pSlct->GetScopePtr();
	ASSERT(pNewScope == pRootObj || pNewScope->IsMyAncestor(*pRootObj));

	Matrix44 trans;
	Object::GetTransMatrix(*pOldScope, *pNewScope, trans);

	// Transform the view's information.
	Vector3d pVec, fVec, tVec;
	view->GetPosition(pVec);
	view->GetOrientation(fVec, tVec);
	fVec.AddVector(pVec);
	tVec.AddVector(pVec);

	trans.Transform(pVec);
	(trans.Transform(fVec)).SubVector(pVec);
	(trans.Transform(tVec)).SubVector(pVec);

	SphrVector oriVec;
	oriVec.NewVector(fVec, tVec);

	view->SetNewView(*pNewScope, *pSlct, true, &pVec,
					  &oriVec, (viewType == VT_3D));
}

// Conversion between device and logical coords.
void QView::DC2LC(const wxPoint &pt, float &x, float &y) const
{
	x = (pt.x - orgX) / zoomVal;
	y = (orgY - pt.y) / zoomVal;
}

void QView::DU2LU(const wxPoint &pt, float &x, float &y) const
{
	x = pt.x / zoomVal;
	y = - pt.y / zoomVal;
}

void QView::LC2DC(float x, float y, wxPoint &pt) const
{
	x = x * zoomVal + orgX;
	pt.x = ROUNDI(x);
	y = orgY - y * zoomVal;
	pt.y = ROUNDI(y);
}

void QView::LC2DC(const Vector3d &vec3D, wxPoint &pt) const
{
	wxSize size = GetViewFrame()->GetClientSize();
	ASSERT(vec3D.GetY() != 0.0f);
	float div = Max(size.GetWidth(), size.GetHeight()) * 0.5f / vec3D.GetY();

	float x = vec3D.GetX() * div + orgX;
	pt.x = ROUNDI(x);
	float y = orgY - vec3D.GetZ() * div;
	pt.y = ROUNDI(y);
}

void QView::LU2DU(float x, float y, wxPoint &pt) const
{
	x = x * zoomVal;
	pt.x = ROUNDI(x);
	y = - y * zoomVal;
	pt.y = ROUNDI(y);
}

// Snapping.
void QView::SetSnapScope(Object &snapScope) {
	Object *pRoot = GetDocument()->GetRootObjectPtr();
	Object::GetTransMatrix(snapScope, *pRoot, tempSnapMatrix);
	Object::GetTransMatrix(*pRoot, snapScope, tempSnapInvMatrix);
}

Vector3d &QView::SnapVector2World(Vector3d &snapVec, const Vector3d *pLockVec) {
	// Transform to world.
	tempSnapMatrix.Transform(snapVec);

	bool rx, ry, rz;
	rx = ry = rz = true;

	if (pLockVec != NULL)
	{
		Vector3d oVec(0.0f, 0.0f, 0.0f);
		Vector3d lVec(*pLockVec);

		tempSnapMatrix.Transform(oVec);
		tempSnapMatrix.Transform(lVec);
		lVec.SubVector(oVec);

		rx = (ROUND3(lVec.GetX()) != 0.0f);
		ry = (ROUND3(lVec.GetY()) != 0.0f);
		rz = (ROUND3(lVec.GetZ()) != 0.0f);
	}

	// Snap it to grid.
	float x, y, z;

	x = snapVec.GetX();
	int gridSnapVal = GetDocument()->GetGridSnapSize();
	if (rx)
	{
		x /= gridSnapVal;
		x = ROUND(x) * gridSnapVal;
	}

	y = snapVec.GetY();
	if (ry)
	{
		y /= gridSnapVal;
		y = ROUND(y) * gridSnapVal;
	}

	z = snapVec.GetZ();
	if (rz)
	{
		z /= gridSnapVal;
		z = ROUND(z) * gridSnapVal;
	}

	// Transform it back.
	tempSnapInvMatrix.Transform(snapVec, x, y, z);

	return snapVec;
}

void QView::SnapAddObjPos(Vector3d &addVec)
{
	if (GetDocument()->GetGridSnap()) {
		SetSnapScope(*(view->GetViewObjPtr()));
		SnapVector2World(addVec);
	}
}

// Snap alignment
void QView::OnEditAlignUI(wxUpdateUIEvent &event)
{
	if(viewType == VT_3D)
	{
		event.Enable(false);
		return;
	}

	Selector *selector = &GetMainFrame()->GetSelector();
	if(selector->GetNumMSelectedObjects() == 0)
		event.Enable(false);
	else
		event.Enable(true);
}

void QView::OnEditAlignSet(wxCommandEvent &event)
{
	// Just snap the op center to grid.
	SetSnapScope(*view->GetViewObjPtr());
	SnapVector2World(opCenterVec);

	QooleDoc *doc = GetDocument();
	QDocHint hint;
	hint.flags = DUAV_QVIEWS;
	doc->UpdateAllViews(NULL, &hint);
}

void QView::OnEditAlignSelection(wxCommandEvent &event)
{
 	ASSERT(viewType != VT_3D);

	// Get selections bounding vectors.
	Object *pNewObj, *pTmpObj = new Object;
	Selector *pSlctr = &GetMainFrame()->GetSelector();
	IterLinkList<ObjectPtr> iter(pSlctr->GetMSelectedObjects());
	iter.Reset();
	while (!iter.IsDone())
	{
		pNewObj = new Object(*(iter.GetNext()->GetPtr()));
		pTmpObj->AddChild(*pNewObj, false);
	}
	pTmpObj->SetBoundRadius();

	Vector3d minVec, maxVec;
	pTmpObj->GetBoundingVectors(minVec, maxVec);

	delete pTmpObj;

	int id = event.GetId();
	if (id == XRCID("menuAlignCenter"))
	{
		opCenterVec.AddVector(minVec, maxVec);
		opCenterVec.MultVector(0.5f);
	}
	else if (id == XRCID("menuAlignTop") || id == XRCID("menuAlignBottom"))
	{
		if (viewType == VT_TOP)
			opCenterVec.SetY(id == XRCID("menuAlignTop") ? maxVec.GetY() : minVec.GetY());
		else
			opCenterVec.SetZ(id == XRCID("menuAlignTop") ? maxVec.GetZ() : minVec.GetZ());
	}
	else
	{
		if (viewType != VT_SIDE)
			opCenterVec.SetX(id == XRCID("menuAlignLeft") ? minVec.GetX() : maxVec.GetX());
		else
			opCenterVec.SetY(id == XRCID("menuAlignLeft") ? minVec.GetY() : maxVec.GetY());
	}

	// Remember view states with new op center.
	ASSERT(qviewsBefore == NULL);
	qviewsBefore = new QViewsState();

	// Find the snap position.
	Vector3d oldPos(opCenterVec), deltaVec;

	SetSnapScope(*view->GetViewObjPtr());
	SnapVector2World(opCenterVec);
	deltaVec.SubVector(opCenterVec, oldPos);

	// Find the move vector.
	Vector3d moveVec;

	if (id == XRCID("menuAlignCenter"))
	{
		SphrVector oriVec;
		view->GetOrientation(oriVec);

		Matrix44 trans;
		trans.SetInvRotate(oriVec);

		moveVec = deltaVec;
		trans.Transform(moveVec);

		oldPos = opCenterVec;
	}
	else if (id == XRCID("menuAlignTop") || id == XRCID("menuAlignBottom"))
	{
		if (viewType == VT_TOP)
		{
			moveVec.NewVector(0.0f, 0.0f, deltaVec.GetY());
			oldPos.SetY(oldPos.GetY() + deltaVec.GetY());
		}
		else
		{
			moveVec.NewVector(0.0f, 0.0f, deltaVec.GetZ());
			oldPos.SetZ(oldPos.GetZ() + deltaVec.GetZ());
		}
	}
	else
	{
		if (viewType != VT_SIDE)
		{
			moveVec.NewVector(deltaVec.GetX(), 0.0f, 0.0f);
			oldPos.SetX(oldPos.GetX() + deltaVec.GetX());
		}
		else
		{
			moveVec.NewVector(deltaVec.GetY(), 0.0f, 0.0f);
			oldPos.SetY(oldPos.GetY() + deltaVec.GetY());
		}
	}
	opCenterVec = oldPos;

	QooleDoc *doc = GetDocument();

	if (moveVec != Vector3d::origVec)
	{
		// Move the selection.
		doc->ObjectsMove(pSlctr->GetMSelectedObjects(), *view, moveVec);

		// Commit the operation.
		OpSelectionMove *op =
			new OpSelectionMove(*view, moveVec, qviewsBefore);
		GetMainFrame()->CommitOperation(*op);
	}
	else
	{
		// Update qviews for the opCenter change.
		QDocHint hint;
		hint.flags = DUAV_QVIEWS;
		doc->UpdateAllViews(NULL, &hint);
		delete qviewsBefore;
	}
	qviewsBefore = NULL;
}

// OpCenter.
#define DRAG_CLICKPOS_EPSILON			5

void QView::FindOpCenterPos(wxPoint &opCenterPos, Vector3d *pCenterVec)
{
	Matrix44 m;
	view->CalInvTransSpaceMatrix(m.SetIdentity());

	Vector3d centerVec;
	m.Transform(centerVec, opCenterVec);

	if (viewType != VT_3D)
		LC2DC(centerVec.GetX(), centerVec.GetZ(), opCenterPos);
	else
		LC2DC(centerVec, opCenterPos);

	if (pCenterVec)
		*pCenterVec = centerVec;
}

void QView::DragOpCenterPos(wxPoint point, bool updateView)
{
	// Need to clip mouse position.
	wxRect rect = GetViewFrame()->GetClientRect();
	if (!rect.Contains(point))
	{
		point.x = Max(rect.GetLeft(), Min(point.x, rect.GetRight()));
		point.y = Max(rect.GetTop(), Min(point.y, rect.GetBottom()));
	}

	// Get the amount of mouse's movement.
	wxPoint mouseMove(point);
	mouseMove -= lastMPos;
	lastMPos = point;

	float x, y;
	DU2LU(mouseMove, x, y);

	// check for edit constraints.
	if (viewType != VT_3D && editConstraint == 1)
		y = 0.0f;   // Horizontal lock.
	else if (viewType != VT_3D && editConstraint == 2)
		x = 0.0f;		// Vertical lock.

	Vector3d moveVec(x, 0.0f, y);

	Matrix44 m;
	view->CalTransSpaceMatrix(m.SetIdentity());
	(m.Transform(moveVec)).SubVector(view->GetPosition());

	tempSnapVec.AddVector(moveVec);

	bool viewChanged = false;
	bool snap = GetDocument()->GetGridSnap() && !wxGetMouseState().ControlDown();

	Vector3d deltaCorner(opCenterVec);
	deltaCorner.SubVector(opCornerVec);
	if (!snap)
	{
		viewChanged = true;

		if(snapCorner)
			opCornerVec = tempSnapVec;
		else
			opCenterVec = tempSnapVec;
	}
	else
	{
		// Edit contraint
		Vector3d lockVec, *pLockVec = NULL;

		if (editConstraint == 1)
			lockVec.NewVector(1.0f, 0.0f, 0.0f);
		else if (editConstraint == 2)
			lockVec.NewVector(0.0f, 0.0f, 1.0f);

		if (viewType != VT_3D && editConstraint != 0)
		{
			pLockVec = &lockVec;
			(m.Transform(lockVec)).SubVector(view->GetPosition());
		}

		// Transform op center pos to abs world coord.
		Vector3d tempNewPos(tempSnapVec);
		SnapVector2World(tempNewPos, pLockVec);

		if (tempNewPos != opCenterVec)
		{
			if(snapCorner)
				opCornerVec = tempNewPos;
			else
				opCenterVec = tempNewPos;
			viewChanged = true;
		}
	}

	// Adjust the center vector.
	if(snapCorner)
	{
		opCenterVec = opCornerVec;
		opCenterVec.AddVector(deltaCorner);
	}

	// Update self.
	if (viewChanged && updateView)
		OnUpdate(NULL, NULL);
}

// Brush Modify.
void QView::UpdateManipDispStat(void)
{
	if (!dispManipDots)
		view->DisplayNoPts();
	else if (modifyBrushMode == OPM_FACE_MOVE)
		view->DisplayFacePts(pManipFace);
	else if (modifyBrushMode == OPM_EDGE_MOVE)
		view->DisplayEdgePts(pManipEdge);
	else if (modifyBrushMode == OPM_VERTEX_MOVE)
		view->DisplayVertexPts(manipVertex);
}


// Operation mode changed.
void QView::OnOpModeChanged(OperationMode opMode, OperationMode prevOpMode)
{
	if (opMode == OPM_MODIFY_BRUSH || opMode == OPM_FACE_MOVE) {
		modifyBrushMode = OPM_FACE_MOVE;
		view->DisplayFacePts();
	}
	else if (opMode == OPM_EDGE_MOVE) {
		modifyBrushMode = OPM_EDGE_MOVE;
		view->DisplayEdgePts();
	}
	else if (opMode == OPM_VERTEX_MOVE) {
		modifyBrushMode = OPM_VERTEX_MOVE;
		view->DisplayVertexPts();
	}
	else
	{
		view->DisplayNoPts();
	}

	if (opMode == OPM_OBJECT_SCALE ||
		opMode == OPM_OBJECT_MULTITOOL)
	{
		drawScaleBoundBox = true;
		CalcScaleManipDots();
	}
	else
	{
		drawScaleBoundBox = false;
	}

	// Op Plane stuff.
	planeDefined = false;
	dragPlanePt = 0;
	clipSide = 0;

	// Update self.
	OnUpdate(NULL, NULL);
}

void QView::UpdateCoordInfo(wxPoint point)
{
	Matrix44 trans;
	Vector3d ov(0.0f, 0.0f, 0.0f);

	trans.SetIdentity();
	view->CalInvTransSpaceMatrix(trans);

	Object *obj = view->GetViewObjPtr();
	while(obj)
	{
		obj->CalInvTransSpaceMatrix(trans);
		obj = obj->GetParentPtr();
	}

	trans.Transform(ov);

	int x = - (int)((orgX - point.x) / zoomVal + ov.GetX());
	int y = - (int)(-(orgY - point.y) / zoomVal + ov.GetZ());

	const wxChar *viewName[] = {
		_("Top"), _("3D View"), _("Back"), _("Side")
	};

	wxString info = wxString::Format(_("%s: (%d, %d)"), viewName[viewType], x, y);
	GetMainFrame()->UpdateStatusBar(info, 1);
}

void QView::UpdateSizeInfo()
{
	// Get the selector.
	Selector *selector = &GetMainFrame()->GetSelector();
	if(selector->GetMSelectedObjects().NumOfElm() == 0)
	{
		// No selected object.
		GetMainFrame()->UpdateStatusBar(wxT(""), 2);
		return;
	}

	// Get selections bounding vectors.
	Object *newObj, *tempObj = new Object;
	IterLinkList<ObjectPtr> iter(selector->GetMSelectedObjects());
	iter.Reset();
	while (!iter.IsDone())
	{
		newObj = new Object(*(iter.GetNext()->GetPtr()));
		tempObj->AddChild(*newObj, false);
	}
	tempObj->SetBoundRadius();

	Vector3d minVec, maxVec;
	tempObj->GetBoundingVectors(minVec, maxVec);

	delete tempObj;

	// Use the min vector as the corner.
	opCornerVec = minVec;

	SetSnapScope(*view->GetViewObjPtr());
	SnapVector2World(opCornerVec);

	// Calculate the size.
	Vector3d size(maxVec);
	size.SubVector(minVec);

	// Update the size display.
	wxString sizeString;
	sizeString.Printf(wxT("Size: (%d, %d, %d)"), ROUNDI(size.GetX()),
			ROUNDI(size.GetY()), ROUNDI(size.GetZ()));
	GetMainFrame()->UpdateStatusBar(sizeString, 2);
}

// Scroll view(s) during operation.
#define UPDATETIME_SCROLLVIEW			100
#define SCROLLVIEW_SCROLLPIXELS			32

void QView::OnQViewScrollView(wxTimerEvent &event)
{
	// Sanity.
	ASSERT(scrollingView);

	wxPoint pt(SCROLLVIEW_SCROLLPIXELS, 0);
	float dist, y;

	DU2LU(pt, dist, y);

	wxRect rect = GetViewFrame()->GetClientRect();

	Vector3d moveVec;
	if (scrollPos.x < rect.GetLeft())
	{
		moveVec.SetX(-dist);
		lastMPos.x += SCROLLVIEW_SCROLLPIXELS;
	}
	else if (scrollPos.x > rect.GetRight())
	{
		moveVec.SetX(dist);
		lastMPos.x -= SCROLLVIEW_SCROLLPIXELS;
	}
	if (scrollPos.y < rect.GetTop())
	{
		moveVec.SetY(dist);
		lastMPos.y += SCROLLVIEW_SCROLLPIXELS;
	}
	else if (scrollPos.y > rect.GetBottom())
	{
		moveVec.SetY(-dist);
		lastMPos.y -= SCROLLVIEW_SCROLLPIXELS;
	}

	downClickVec.SubVector(moveVec);
	moveVec.NewVector(moveVec.GetX(), 0.0f, moveVec.GetY());
	Vector3d oldMoveVec(moveVec);

	if (GetMainFrame()->IsLockedView(this))
	{
		// move affects other views.
		Matrix44 trans;
		trans.SetRotate(view->GetOrientation());
		trans.Transform(moveVec);

		Vector3d editPos = GetMainFrame()->GetEditFocusPos();
		editPos.AddVector(moveVec);
		GetMainFrame()->SetEditFocusPos(editPos, false);
	}
	else
	{
		// Just move self.
		view->MoveRelPosition(moveVec);
	}

	wxMouseEvent mev;
	mev.m_x = scrollPos.x;
	mev.m_y = scrollPos.x;
	if (scrollingView == OPM_OBJECT_SELECT)
	{
		OnMMObjectsSelect(mev);
	}
	else if (scrollingView == OPM_OBJECT_MOVE)
	{
		OnMMObjectsMove(mev);
		// For snapping.
		view->CalInvTransSpaceMatrix(tempInvViewMatrix.SetIdentity());
	}
	else if (scrollingView == OPM_OBJECT_SCALE)
	{
		OnMMObjectsScale(mev);
	}
	else if (scrollingView == OPM_MODIFY_BRUSH)
	{
		tempSnapVecTtl.SubVector(oldMoveVec);
		tempSnapVec.SubVector(oldMoveVec);
		OnMMModifyBrush(mev);
	}
}

// Timer Update Unfocused QViews
#define UPDATETIME_RENDERQVIEWS			50
void QView::SetTimerUpdateQViews(bool set)
{
	if(set)
		timerRenderViews.Start(UPDATETIME_RENDERQVIEWS, wxTIMER_CONTINUOUS);
	else
		timerRenderViews.Stop();
}

void QView::OnTimerUpdateQViews(wxTimerEvent &event)
{
	QooleDoc *pDoc = GetDocument();
	QDocHint hint;
	hint.flags = DUAV_QVIEWS;
	pDoc->UpdateAllViews(this, &hint);
}

// Mode events.
#define UPDATETIME_DRAGSEL				500

void QView::OnLMBDObjectsSelect(wxMouseEvent &event)
{
	scrollingView = 0;
	selectPending = false;
	selectAddPending = false;
	dragSelect = false;
	dragAddSelect = false;
	reverseSelect = event.AltDown();

	Selector *pSlct = &(GetMainFrame()->GetSelector());

	if (!event.ControlDown() && !event.ShiftDown())
	{
		// Deal with selection at button release.
		selectPending = true;
	}
	else if (event.ControlDown())
	{
		// Cycle the Ctrl-Add hi-lite obj.
		selectAddPending = true;

		Object *pSelObj;
		float x = downClickVec.GetX();
		float y = downClickVec.GetY();

		if (!reverseSelect)
			pSelObj = view->SingleForwardSelect(x, y);
		else
			pSelObj = view->SingleReverseSelect(x, y);
		pSlct->SSelectObject(pSelObj);

		// Update all qviews.
		QooleDoc *doc = GetDocument();
		QDocHint hint;
		hint.flags = DUAV_OBJSSEL | DUAV_QVIEWS;
		hint.scope = pSlct->GetScopePtr();
		doc->UpdateAllViews(NULL, &hint);
	}
	else if (event.ShiftDown())
	{
		// Drag box to add objs to selection.
		dragAddSelect = true;

		// Remember the old selection.
		ASSERT(oldSelection.NumOfElm() == 0);
		oldSelection = pSlct->GetMSelectedObjects();
	}
}

void QView::OnMMObjectsSelect(wxMouseEvent &event)
{
	if (!mouseLDrag)
		return;

	if (selectPending)
		selectPending = false;

	if (GetMainFrame()->GetOpMode() == OPM_OBJECT_SELECT  ||
		(GetMainFrame()->GetOpMode() == OPM_OBJECT_MULTITOOL &&
		 subOperationMode == OPM_OBJECT_SELECT) &&
		!dragSelect && !dragAddSelect && !selectAddPending)
	{

		// Drag box to set new selection
		dragSelect = true;

		// Remember old selection.
		ASSERT(oldSelection.NumOfElm() == 0);
		Selector *pSlct = &GetMainFrame()->GetSelector();
		oldSelection = pSlct->GetMSelectedObjects();
	}

	if (!dragSelect && !dragAddSelect)
		return;

	// Need to scroll the view(s) and clip coord
	//  if mouse is outside the window.
	wxRect rect = GetViewFrame()->GetClientRect();

	wxPoint point = event.GetPosition();
	if (!rect.Contains(point))
	{
		// Set the scroll update timer.
		if (!scrollingView && viewType != VT_3D)
		{
			scrollingView = OPM_OBJECT_SELECT;
			timerScrollView.Start(UPDATETIME_SCROLLVIEW, wxTIMER_CONTINUOUS);
		}
		// Used to determine direction of scrolling.
		scrollPos = point;

		// Need to clip the release coord.
		point.x = Max(rect.GetLeft(), Min(point.x, rect.GetRight()));
		point.y = Max(rect.GetTop(), Min(point.y, rect.GetBottom()));
	}
	else
	{
		if (scrollingView)
		{
			// Kill the scroll update timer
			timerScrollView.Stop();
			scrollingView = 0;
		}

		// Reset the idle timer to update drag selection.
		timerUpdateDragSel.Start(UPDATETIME_DRAGSEL, wxTIMER_CONTINUOUS);
	}

	// Convert down and up click pts.
	LC2DC(downClickVec.GetX(), downClickVec.GetY(), ptClick);

	float x, y;
	DC2LC(point, x, y);
	upClickVec.NewVector(x, y, 0.0f);

	// Need to update drag box's drawing.
	int x1, y1, x2, y2;
	x1 = Min(ptClick.x, point.x);
	y1 = Min(ptClick.y, point.y);
	x2 = Max(ptClick.x, point.x);
	y2 = Max(ptClick.y, point.y);

	dragBoxRect = wxRect(x1, y1, x2 - x1, y2 - y1);
	QDocHint hint;
	hint.flags = DUQV_DISPLAYDRAGBOX;
	OnUpdate(NULL, &hint);
}

void QView::OnLMBUObjectsSelect(wxMouseEvent &event)
{
	if (!selectPending && !dragSelect && !dragAddSelect)
		return;

	// Disable scrolling.
	if (scrollingView)
	{
		scrollingView = 0;
		timerScrollView.Stop();
	}

	Selector *pSlct = &GetMainFrame()->GetSelector();

	if (selectPending)
	{
		// New selection.
		ASSERT(!mouseLDrag);
		ASSERT(pSlct->GetSSelectedObject() == NULL);

		Object *pSelObj;
		float x, y;
		DC2LC(event.GetPosition(), x, y);

		if (!reverseSelect)
			pSelObj = view->MultiForwardSelect(x, y);
		else
			pSelObj = view->MultiReverseSelect(x, y);

		// Clear the sel buf.
		selectObjectsBuffer.DeleteAllNodes();
		if (pSelObj != NULL)
			selectObjectsBuffer.AppendNode(*(new ObjectPtr(pSelObj)));

		// Commit the operation.
		OpSelectNewObjs *op = new OpSelectNewObjs(selectObjectsBuffer);
		GetMainFrame()->CommitOperation(*op);
	}
	else if (dragSelect)
	{
		// Update selection buffer one last time.
		wxTimerEvent ev;
		OnUpdateDragSelect(ev);

		// Restore the old selector.
		pSlct->MSelectObjects(oldSelection);
		oldSelection.DeleteAllNodes();

		// Commit the operation.
		OpSelectNewObjs *op = new OpSelectNewObjs(selectObjectsBuffer);
		GetMainFrame()->CommitOperation(*op);
	}
	else if (dragAddSelect)
	{
		// Update the selection buffer one last time.
		wxTimerEvent ev;
		OnUpdateDragSelect(ev);

		// Restore the old selector.
		pSlct->MSelectObjects(oldSelection);
		oldSelection.DeleteAllNodes();

		// Commit the operation.
		if (selectObjectsBuffer.NumOfElm() > 0)
		{
			OpSelectAddObjs *op = new OpSelectAddObjs(selectObjectsBuffer);
			GetMainFrame()->CommitOperation(*op);
		}
		else
		{
			// Need to update self view to clear the drag box.
			OnUpdate(NULL, NULL);
		}
	}

	selectPending = false;
	dragSelect = false;
	dragAddSelect = false;
}

void QView::OnUpdateDragSelect(wxTimerEvent &event)
{
	// Sanity.
	ASSERT(dragSelect || dragAddSelect);

	// Kill the old timer.
	timerUpdateDragSel.Stop();

	Selector *pSlct = &GetMainFrame()->GetSelector();
	float x1, x2, y1, y2;

	// Get all the objs inside the drag box.
	x1 = Min(downClickVec.GetX(), upClickVec.GetX());
	y1 = Min(downClickVec.GetY(), upClickVec.GetY());
	x2 = Max(downClickVec.GetX(), upClickVec.GetX());
	y2 = Max(downClickVec.GetY(), upClickVec.GetY());

	selectObjectsBuffer.DeleteAllNodes();
	view->DragSelectMultiObjects(x1, y1, x2, y2, selectObjectsBuffer);

	LinkList<ObjectPtr> tmpSlctBuf;

	if (dragSelect)
		tmpSlctBuf = selectObjectsBuffer;

	if (dragAddSelect)
	{
		// Remove duplicate selected objs from slctObjsBuf.
		ObjectPtr *pObjPtr;
		IterLinkList<ObjectPtr> iterNew(selectObjectsBuffer);
		IterLinkList<ObjectPtr> iterOld(oldSelection);
		iterNew.Reset();
		while (!iterNew.IsDone()) {
			pObjPtr = iterNew.GetNext();
			iterOld.Reset();
			while (!iterOld.IsDone()) {
				if (iterOld.GetNext()->GetPtr() == pObjPtr->GetPtr()) {
					delete &(selectObjectsBuffer.RemoveNode(*pObjPtr));
					break;
				}
			}
		}

		tmpSlctBuf = selectObjectsBuffer;

		// Add the original selection back into the select buf.
		LinkList<ObjectPtr> tmpOldBuf(oldSelection);
		while (tmpOldBuf.NumOfElm() > 0)
			tmpSlctBuf.AppendNode(tmpOldBuf.RemoveNode(0));
	}

	// Select the objs.
	pSlct->MSelectObjects(tmpSlctBuf);
	tmpSlctBuf.DeleteAllNodes();

	// Update the size info.
	UpdateSizeInfo();

	// Update self.
	QDocHint hint;
	hint.flags = DUQV_DISPLAYDRAGBOX;
	OnUpdate(NULL, &hint);

	// Update other qviews.
	QooleDoc *pDoc = GetDocument();
	hint.flags = DUAV_OBJSSEL | DUAV_QVIEWS;
	hint.scope = pSlct->GetScopePtr();
	pDoc->UpdateAllViews(this, &hint);
}

// Move / Rotate / Scale
void QView::OnLMBDObjectsMoveRotateScale(wxMouseEvent &event)
{
	OnLMBDObjectsSelect(event);

	scrollingView = 0;
	operatePending = false;
	dragOperate = false;
	dragOpCenter = false;

	if (selectAddPending || dragAddSelect)
		return;

	lastMPos = ptClick;

	// Remember op center for scale operation.
	oldOpCenterVec = opCenterVec;

	// Test for click on manip dots in scale mode.
	if (GetMainFrame()->GetOpMode() == OPM_OBJECT_SCALE)
	{
		drawScaleBoundBox = false;
		dragScaleManipDot = false;

		wxPoint mPos;
		for(int i = 0; i < 8; i++)
		{
			if (viewType != VT_3D)
				LC2DC(scaleManipDots[i].GetX(), scaleManipDots[i].GetZ(), mPos);
			else
			{
				// Invalid scale.
				if(scaleManipDots[i].GetY() == 0)
					continue;
				LC2DC(scaleManipDots[i], mPos);
			}

			mPos -= event.GetPosition();
			if (ABS(mPos.x) <= DRAG_CLICKPOS_EPSILON &&
				ABS(mPos.y) <= DRAG_CLICKPOS_EPSILON) {
				Matrix44 m;
				view->CalTransSpaceMatrix(m.SetIdentity());
				i = (i + 4) % 8;
				m.Transform(opCenterVec, scaleManipDots[i]);
				dragScaleManipDot = true;
				operatePending = true;
				return;
			}
		}
	}

	// Test for click on op center.
	wxPoint opCenterPos;
	FindOpCenterPos(opCenterPos);
	wxPoint point = event.GetPosition() - opCenterPos;

	if (GetMainFrame()->GetOpMode() != OPM_OBJECT_MOVE &&
		ABS(point.x) <= DRAG_CLICKPOS_EPSILON  &&
		ABS(point.y) <= DRAG_CLICKPOS_EPSILON)
	{
		dragOpCenter = true;
		selectPending = false;

		// Need to turn on timer rendering for the unfocused qviews.
		SetTimerUpdateQViews(true);

		// Set up snapping stuff.
		tempSnapVec = opCenterVec;
		SetSnapScope(*(view->GetViewObjPtr()));
	}
	else if (view->IsMultiSelectionHit(downClickVec.GetX(),
										downClickVec.GetY())) {
		operatePending = true;
	}
}

void QView::OnMMObjectsMove(wxMouseEvent &event)
{
	OnMMObjectsSelect(event);

	// Sanity.
	ASSERT(!dragSelect);

	// Used to snap the op center upon opcenter drag.
	// if (dragOpCenter) // Need to update opCenter's display.
	// {
	// 	DragOpCenterPos(point);
	// 	return;
	// }

	if (operatePending)
	{
		selectPending = false;
		operatePending = false;
		dragOperate = true;

		// Need to turn on timer rendering for the unfocused qviews.
		SetTimerUpdateQViews(true);

		// Turn on wireframe approximation rendering.
		View::ApproxItems(true);

		// Set up snapping stuff.
		tempSnapVec = snapCorner ? opCornerVec : opCenterVec;
		tempSnapVecTtl.NewVector(0.0f, 0.0f, 0.0f);
		SetSnapScope(*(view->GetViewObjPtr()));
		view->CalInvTransSpaceMatrix(tempInvViewMatrix.SetIdentity());

		// Snap the op center to grid.
		// DragOpCenterPos(point);

		// Update the status bar
		GetMainFrame()->UpdateStatusBar(_("Press the Ctrl key to prevent snapping"));

		// Remember view states.
		ASSERT(qviewsBefore == NULL);
		qviewsBefore = new QViewsState();

		// Duplicate drag.
		if (event.ControlDown())
		{
			Vector3d dupVec(0.0f, 0.0f, 0.0f);
			OpEditDuplicate *op = new OpEditDuplicate(dupVec);
			GetMainFrame()->CommitOperation(*op);
		}
	}

	if (!dragOperate)
		return;

	// Clip the mouse coord and scroll view.
	wxRect rect = GetViewFrame()->GetClientRect();
	wxPoint point = event.GetPosition();
	if (!rect.Contains(point))
	{
		if (!scrollingView)
		{
			// TODO: check this
			scrollingView = OPM_OBJECT_MOVE;//ID_MODE_OBJECTMOVE;
			timerScrollView.Start(UPDATETIME_SCROLLVIEW, wxTIMER_CONTINUOUS);
		}
		scrollPos = point;

		// clip.
		point.x = Max(rect.GetLeft(), Min(point.x, rect.GetRight()));
		point.y = Max(rect.GetTop(), Min(point.y, rect.GetBottom()));
	}
	else if (scrollingView)
	{
		// stop scrolling view.
		timerScrollView.Stop();
		scrollingView = 0;
	}

	// Move the op center first.
	Vector3d oldOpPos(opCenterVec);
	DragOpCenterPos(point, false);

	if (!(oldOpPos == opCenterVec))
	{
		Vector3d deltaVec(opCenterVec);
		tempInvViewMatrix.Transform(deltaVec);
		tempInvViewMatrix.Transform(oldOpPos);
		deltaVec.SubVector(oldOpPos);

		QooleDoc *pDoc = GetDocument();
		Selector *pSlct = &GetMainFrame()->GetSelector();

		// Move the selected objs.
		pDoc->ObjectsMove(pSlct->GetMSelectedObjects(),
						  *view, deltaVec, false);
		tempSnapVecTtl.AddVector(deltaVec);

		// Update only self.
		OnUpdate(NULL, NULL);
	}
}

void QView::OnLMBUObjectsMove(wxMouseEvent &event)
{
	OnLMBUObjectsSelect(event);

	if (!dragOperate && !dragOpCenter)
		return;

	// Need to turn off timer rendering for unfocused qviews.
	SetTimerUpdateQViews(false);

	// Turn off wireframe approximation rendering.
	View::ApproxItems(false);

	if (dragOpCenter)
	{
		// Do one last update on the other qviews.
		wxTimerEvent ev;
		OnTimerUpdateQViews(ev);
		dragOpCenter = false;
		return;
	}

	if (scrollingView) // Turn off scrolling.
	{
		scrollingView = 0;
		timerScrollView.Stop();
	}

	Selector *pSlct = &GetMainFrame()->GetSelector();
	QooleDoc *pDoc = GetDocument();

	// Commit the operation.
	OpSelectionMove *op =
		new OpSelectionMove(*view, tempSnapVecTtl, qviewsBefore);
	GetMainFrame()->CommitOperation(*op);
	qviewsBefore = NULL;

	// Update all views.
	QDocHint hint;
	hint.flags = DUAV_OBJSMODATTRIB;
	hint.scope = pSlct->GetScopePtr();
	pDoc->UpdateAllViews(NULL, &hint);

	dragOperate = false;

	// Update the status bar
	GetMainFrame()->UpdateStatusBar(_("Ready"));
}

void QView::OnMMObjectsRotate(wxMouseEvent &event)
{
	OnMMObjectsSelect(event);

	// Sanity.
	ASSERT(!dragSelect);

	wxPoint point = event.GetPosition();
	if (dragOpCenter) // Need to update the opCenter's display.
	{
		DragOpCenterPos(point);
		return;
	}

	if (operatePending)
	{
		selectPending = false;
		operatePending = false;
		dragOperate = true;

		// Need to turn on timer rendering for the unfocused qviews.
		SetTimerUpdateQViews(true);

		// Turn on wireframe approximation rendering.
		View::ApproxItems(true);

		// Snapping.
		tempSnapRotVal = 0.0f;
		tempLastSnapRotVal = 0.0f;

		GetMainFrame()->UpdateStatusBar(_("Press the Ctrl key to prevent snapping"));

		// qvs
		ASSERT(qviewsBefore== NULL);
		qviewsBefore = new QViewsState();
	}

	if (!dragOperate)
		return;

	wxPoint opCenterPos;
	Vector3d rotPt;
	FindOpCenterPos(opCenterPos, &rotPt);

	wxPoint lastVec(lastMPos);
	lastVec -= opCenterPos;

	lastMPos = point;
	point -= opCenterPos;

	float rotAngle = ATan((float) -lastVec.y, (float) lastVec.x) -
						ATan((float) -point.y, (float) point.x);
	tempSnapRotVal += rotAngle;

	bool snap = GetDocument()->GetRotSnap() && !event.ControlDown();

	if (snap)
	{
		int rotSnapVal = GetDocument()->GetRotSnapSize();
		float toBe = tempSnapRotVal / DEG2RAD(rotSnapVal);
		toBe = ROUND(toBe) * DEG2RAD(rotSnapVal);
		rotAngle = toBe - tempLastSnapRotVal;
	}
	else
	{
		rotAngle = tempSnapRotVal - tempLastSnapRotVal;
	}
	tempLastSnapRotVal += rotAngle;

	// Rotate the selected objs.
	QooleDoc *pDoc = GetDocument();
	Selector *pSlct = &GetMainFrame()->GetSelector();
	pDoc->ObjectsRotate(pSlct->GetMSelectedObjects(),
						*view, rotPt, rotAngle, false);

	// Update only self.
	OnUpdate(NULL, NULL);
}

void QView::OnLMBUObjectsRotate(wxMouseEvent &event)
{
	OnLMBUObjectsSelect(event);

	if (!dragOperate && !dragOpCenter)
		return;

	// Need to turn off timer rendering for unfocused qviews.
	SetTimerUpdateQViews(false);

	// Turn off wireframe approximation rendering.
	View::ApproxItems(false);

	wxPoint point = event.GetPosition();
	if (dragOpCenter)
	{
		// Do one last update on the other qviews.
		wxTimerEvent ev;
		OnTimerUpdateQViews(ev);
		dragOpCenter = false;
		return;
	}

	wxPoint opCenterPos;
	Vector3d rotPt;
	FindOpCenterPos(opCenterPos, &rotPt);

	// Commit the operation.
	Selector *pSlct = &(GetMainFrame()->GetSelector());
	QooleDoc *pDoc = GetDocument();

	OpSelectionRotate *op =
		new OpSelectionRotate(*view, rotPt, tempLastSnapRotVal, qviewsBefore);
	GetMainFrame()->CommitOperation(*op);
	qviewsBefore = NULL;

	// Update all views.
	QDocHint hint;
	hint.flags = DUAV_OBJSMODATTRIB;
	hint.scope = pSlct->GetScopePtr();
	pDoc->UpdateAllViews(NULL, &hint);

	dragOperate = false;

	GetMainFrame()->UpdateStatusBar(_("Ready"));
}

void QView::OnMMObjectsScale(wxMouseEvent &event)
{
	OnMMObjectsSelect(event);

	// Sanity.
	ASSERT(!dragSelect);

	wxPoint point = event.GetPosition();
	if (dragOpCenter) // Need to update the opCenter's display.
	{
		DragOpCenterPos(point);
		return;
	}

	if (!dragOperate && !operatePending)
		return;

	// Get the standard ref ptrs.
	QooleDoc* pDoc = GetDocument();
	Selector *pSlct = &GetMainFrame()->GetSelector();

	// Convert opCenter to view space.
	wxPoint opCenterPos;
	Vector3d basisVec;
	FindOpCenterPos(opCenterPos, &basisVec);

	if (operatePending)
	{
		selectPending = false;
		operatePending = false;
		dragOperate = true;

		// Need to turn on timer rendering for unfocused qviews.
		SetTimerUpdateQViews(true);

		// Turn on wireframe approximation rendering.
		View::ApproxItems(true);

		// Begin the scale operation.
		Object *pOpObj;
		pOpObj = &pDoc->ObjectsScaleBegin(pSlct->GetMSelectedObjects(),
										   *view, basisVec);
		// Set up snapping stuff.
		SetSnapScope(*pOpObj);
		tempScaleVec.NewVector(1.0f, 1.0f, 1.0f);
		tempSnapBoundVec1 = pOpObj->GetMinBoundVec();
		tempSnapBoundVec2 = pOpObj->GetMaxBoundVec();

		// Update the status bar
		GetMainFrame()->UpdateStatusBar(_("Press the Ctrl key to prevent snapping"));

		// Remember view states.
		Vector3d tmpVec(opCenterVec);
		opCenterVec = oldOpCenterVec;

		ASSERT(qviewsBefore == NULL);
		qviewsBefore = new QViewsState();

		opCenterVec = tmpVec;
	}

	// Clip the mouse coord and scroll view.
	wxRect rect = GetViewFrame()->GetClientRect();
	if (!rect.Contains(point))
	{
		if (!scrollingView)
		{
			scrollingView = OPM_OBJECT_SCALE;
			timerScrollView.Start(UPDATETIME_SCROLLVIEW, wxTIMER_CONTINUOUS);
		}
		scrollPos = point;

		point.x = Max(rect.GetLeft(), Min(point.x, rect.GetRight()));
		point.y = Max(rect.GetTop(), Min(point.y, rect.GetBottom()));
	}
	else // stop scrolling view.
	{
		timerScrollView.Stop();
		scrollingView = 0;
	}

	// Get the downclick vec and the current mouse vec.
	LC2DC(downClickVec.GetX(), downClickVec.GetY(), ptClick);
	ptClick -= opCenterPos;
	point -= opCenterPos;

	// Calculate the new scale vec.
	float sx, sz;

	if (ABS(ptClick.x) < 5)  // Disable horizontal scaling.
		sx = 1.0f;
	else if (editConstraint == 2) // Vertical lock.  Disable horizontal scale.
		sx = 1.0f;
	else {
		if (point.x * ptClick.x < 0 || ABS(point.x) < 5)
			point.x = (ptClick.x < 0 ? -5 : 5);  // Prevent negative scaling.
		sx = ((float) point.x) / ptClick.x;
	}

	if (ABS(ptClick.y) < 5) // Disable vertical scaling.
		sz = 1.0f;
	else if (editConstraint == 1) // Horizontal lock.  Disable Vertical Scale.
		sz = 1.0f;
	else {
		if (point.y * ptClick.y < 0 || ABS(point.y) < 5)
			point.y = (ptClick.y < 0 ? -5 : 5);  // Prevent negative scaling.
		sz = ((float) point.y) / ptClick.y;
	}

	bool snap = GetDocument()->GetGridSnap() && !event.ControlDown();
	if (snap)
	{
		// Snap min bounding vec.
		Vector3d newBoundVec1(tempSnapBoundVec1.GetX() * sx,
							  tempSnapBoundVec1.GetY(),
							  tempSnapBoundVec1.GetZ() * sz);
		SnapVector2World(newBoundVec1);

		// Snap max bounding vec.
		Vector3d newBoundVec2(tempSnapBoundVec2.GetX() * sx,
							  tempSnapBoundVec2.GetY(),
							  tempSnapBoundVec2.GetZ() * sz);
		SnapVector2World(newBoundVec2);

		// Derive new scale vector.
		if (sx != 1.0f) {
			if (ptClick.x < 0) {
				sx = (newBoundVec1.GetX() >= -1.0f ?
					  tempScaleVec.GetX() :
					  newBoundVec1.GetX() / tempSnapBoundVec1.GetX());
			}
			else {
				sx = (newBoundVec2.GetX() < 1.0f ?
					  tempScaleVec.GetX() :
					  newBoundVec2.GetX() / tempSnapBoundVec2.GetX());
			}
		}

		if (sz != 1.0f) {
			if (ptClick.y > 0) {
				sz = (newBoundVec1.GetZ() >= -1.0f ?
					  tempScaleVec.GetZ() :
					  newBoundVec1.GetZ() / tempSnapBoundVec1.GetZ());
			}
			else {
				sz = (newBoundVec2.GetZ() < 1.0f ?
					  tempScaleVec.GetZ() :
					  newBoundVec2.GetZ() / tempSnapBoundVec2.GetZ());
			}
		}
	}

	tempScaleVec.NewVector(sx, 1.0f, sz);

	// Set the new scale.
	pDoc->ObjectsScaleChange(pSlct->GetMSelectedObjects(), tempScaleVec, false);

	// Update only self.
	OnUpdate(NULL, NULL);
}

void QView::OnLMBUObjectsScale(wxMouseEvent &event)
{
	drawScaleBoundBox = true;
	OnLMBUObjectsSelect(event);

	if (operatePending)
	{
		ASSERT(!dragOperate);
		operatePending = false;
		opCenterVec = oldOpCenterVec;
	}

	if (!dragOperate && !dragOpCenter)
		return;

	// Need to turn off timer rendering for unfocused qviews.
	SetTimerUpdateQViews(false);

	// Turn off wireframe approximation rendering.
	View::ApproxItems(false);

	if (dragOpCenter)
	{
		// Do one last update on the other qviews.
		wxTimerEvent ev;
		OnTimerUpdateQViews(ev);

		// Update self for the scale bounding box.
		OnUpdate(NULL, NULL);

		dragOpCenter = false;
		return;
	}

	if (scrollingView) // Turn off scrolling.
	{
		scrollingView = 0;
		timerScrollView.Stop();
	}

	// Get standard ref ptrs.
	Selector *pSlct = &GetMainFrame()->GetSelector();
	QooleDoc *pDoc = GetDocument();

	// End the scale operation.
	pDoc->ObjectsScaleEnd(pSlct->GetMSelectedObjects(), false);

	wxPoint opCenterPos;
	Vector3d basisVec;
	FindOpCenterPos(opCenterPos, &basisVec);

	// Swap the op center pos back.
	if (!dragScaleManipDot)
		opCenterVec = oldOpCenterVec;
	else
		pSlct->GetMSelectedObjectsCenter(opCenterVec);

	// Update the size info.
	UpdateSizeInfo();

	// Commit the operation.
	OpSelectionScale *op =
		new OpSelectionScale(*view, basisVec, tempScaleVec, qviewsBefore);
	GetMainFrame()->CommitOperation(*op);
	qviewsBefore = NULL;

	// Update all QViews.. Hack. use DUAV_OBJSSEL to update manip dots.
	QDocHint hint;
	hint.flags = DUAV_QVIEWS | DUAV_OBJSSEL;
	pDoc->UpdateAllViews(NULL, &hint);

	// Update the rest.
	hint.flags = DUAV_OBJSMODATTRIB | DUAV_NOQVIEWS;
	hint.scope = pSlct->GetScopePtr();
	pDoc->UpdateAllViews(NULL, &hint);

	dragOperate = false;

	// Update the status bar
	GetMainFrame()->UpdateStatusBar(_("Ready"));
}

void QView::OnLMBDObjectsMultiTool(wxMouseEvent &event)
{
	// Select the object.
	OnLMBDObjectsSelect(event);

	subOperationMode = OPM_OBJECT_SELECT;
	if (selectAddPending || dragAddSelect)
		return;

	lastMPos = ptClick;

	// Remember op center for scale operation.
	oldOpCenterVec = opCenterVec;

	drawScaleBoundBox = false;
	dragScaleManipDot = false;

	// Check for scaling.
	wxPoint mPos;
	for(int i = 0; i < 8; i++)
	{
		if (viewType != VT_3D)
				LC2DC(scaleManipDots[i].GetX(), scaleManipDots[i].GetZ(), mPos);
		else
		{
			// Invalid scale.
			if(scaleManipDots[i].GetY() == 0)
				continue;
			LC2DC(scaleManipDots[i], mPos);
		}

		mPos -= event.GetPosition();
		if (ABS(mPos.x) <= DRAG_CLICKPOS_EPSILON &&
			ABS(mPos.y) <= DRAG_CLICKPOS_EPSILON)
		{
			Matrix44 m;
			view->CalTransSpaceMatrix(m.SetIdentity());
			i = (i + 4) % 8;
			m.Transform(opCenterVec, scaleManipDots[i]);
			scrollingView = 0;
			dragOpCenter = false;
			dragOperate = false;
			dragScaleManipDot = true;
			operatePending = true;
			subOperationMode = OPM_OBJECT_SCALE;
			return;
		}
	}

	// Check for move sub operation.
	if (view->IsMultiSelectionHit(downClickVec.GetX(), downClickVec.GetY()))
	{
		operatePending = true;
		subOperationMode = OPM_OBJECT_MOVE;

		// Use a corner insted of the center for snapping.
		snapCorner = true;
	}

	// Disable something from the select operate mode.
	if(subOperationMode != OPM_OBJECT_SELECT)
	{
		scrollingView = 0;
		dragOperate = false;
		dragOpCenter = false;
	}
}

void QView::OnMMObjectsMultiTool(wxMouseEvent &event)
{
	// Delegate to the correct sub operation mode.
	switch(subOperationMode)
	{
	case OPM_OBJECT_SELECT:
		OnMMObjectsSelect(event);
		break;
	case OPM_OBJECT_MOVE:
		OnMMObjectsMove(event);
		break;
	case OPM_OBJECT_SCALE:
		OnMMObjectsScale(event);
		break;
	case OPM_OBJECT_ROTATE:
		OnMMObjectsRotate(event);
		break;
	default:
		break;
	}
}

void QView::OnLMBUObjectsMultiTool(wxMouseEvent &event)
{
	// Enable scale drawing.
	drawScaleBoundBox = true;

	// Delegate to the correct sub operation mode.
	switch(subOperationMode)
	{
	case OPM_OBJECT_SELECT:
		OnLMBUObjectsSelect(event);
		break;
	case OPM_OBJECT_MOVE:
		OnLMBUObjectsMove(event);
		break;
	case OPM_OBJECT_SCALE:
		OnLMBUObjectsScale(event);
		break;
	case OPM_OBJECT_ROTATE:
		OnLMBUObjectsRotate(event);
		break;
	default:
		break;
	}

	// Unset the snap corner.
	snapCorner = false;
}

// Brush modify
void QView::OnLMBDModifyBrush(wxMouseEvent &event)
{
	dragOperate = false;
	pManipFace = NULL;
	pManipEdge = NULL;
	manipVertex = -1;
	dispManipDots = false;

	wxPoint point = event.GetPosition();

	// Get standard ptrs.
	QooleDoc* pDoc = GetDocument();
	Selector *pSlct = &GetMainFrame()->GetSelector();

	// Check if the selected object has a brush.
	if (pSlct->GetNumMSelectedObjects() != 1 ||
		!(pSlct->GetMSelectedObjects())[0].GetPtr()->HasBrush()) {
		return;
	}

	Object *pBrushObj = (pSlct->GetMSelectedObjects())[0].GetPtr();

	// Clip mouse coord.
	wxRect rect = GetViewFrame()->GetClientRect();
	if (!rect.Contains(point))
	{
		point.x = Max(rect.GetLeft(), Min(point.x, rect.GetRight()));
		point.y = Max(rect.GetTop(), Min(point.y, rect.GetBottom()));
	}

	// Test if clicked on a manip dot.
	float x, y;
	DC2LC(point, x, y);
	float epsilon = DRAG_CLICKPOS_EPSILON / zoomVal;

	if ((modifyBrushMode == OPM_FACE_MOVE &&
		 (pManipFace = view->SelectFace(x, y, epsilon, &tempSnapVec)) != NULL) ||
		(modifyBrushMode == OPM_EDGE_MOVE &&
		 (pManipEdge = view->SelectEdge(x, y, epsilon, &tempSnapVec)) != NULL) ||
		(modifyBrushMode == OPM_VERTEX_MOVE &&
		 (manipVertex = view->SelectVertex(x, y, epsilon, &tempSnapVec)) != -1))
	{
		// Init manipulation stuff.
		dragOperate = true;
		scrollingView = 0;
		lastMPos = ptClick;

		// Init snapping
		SetSnapScope(*(view->GetViewObjPtr()));
		tempSnapVecTtl = tempSnapVec;

		// Turn on timer rendering.
		dispManipDots = true;
		SetTimerUpdateQViews(true);

		// Turn on wireframe approximation rendering.
		View::ApproxItems(true);

		pDoc->BrushManipulateBegin(*pBrushObj);

		// Update the status bar
		GetMainFrame()->UpdateStatusBar(_("Press the Ctrl key to prevent snapping"));

		// Remember view states.
		ASSERT(qviewsBefore == NULL);
		qviewsBefore = new QViewsState();
	}

	// Update all Qviews to display the dragging manip dot.
	QDocHint hint;
	hint.flags = DUAV_QVIEWS | DUQV_UPDATEMANIPSTAT;
	pDoc->UpdateAllViews(NULL, &hint);
}

void QView::OnMMModifyBrush(wxMouseEvent &event)
{
	if (!dragOperate)
		return;

	QooleDoc* pDoc = GetDocument();

	// Clip the mouse coord and scroll view.
	wxPoint point = event.GetPosition();
	wxRect rect = GetViewFrame()->GetClientRect();
	if (!rect.Contains(point))
	{
		if (!scrollingView)
		{
			scrollingView = OPM_MODIFY_BRUSH;
			timerScrollView.Start(UPDATETIME_SCROLLVIEW, wxTIMER_CONTINUOUS);
		}
		scrollPos = point;

		// clip.
		point.x = Max(rect.GetLeft(), Min(point.x, rect.GetRight()));
		point.y = Max(rect.GetTop(), Min(point.y, rect.GetBottom()));
	}
	else if (scrollingView) // stop scrolling view.
	{
		timerScrollView.Stop();
		scrollingView = 0;
	}

	wxPoint mouseMove(point);
	mouseMove -= lastMPos;
	lastMPos = point;

	float x, y;
	DU2LU(mouseMove, x, y);

	// check for edit constraints.
	if (viewType != VT_3D && editConstraint == 1)
		y = 0.0f;   // Horizontal lock.
	else if (viewType != VT_3D && editConstraint == 2)
		x = 0.0f;		// Vertical lock.

	Vector3d moveVec(x, 0.0f, y);

	tempSnapVecTtl.AddVector(moveVec);
	moveVec = tempSnapVecTtl;

	bool snap = GetDocument()->GetGridSnap() && !event.ControlDown();
	if (snap)
	{
		Matrix44 m;
		view->CalTransSpaceMatrix(m.SetIdentity());
		m.Transform(moveVec);

		// Edit contraint
		Vector3d lockVec, *pLockVec = NULL;

		if (editConstraint == 1)
			lockVec.NewVector(1.0f, 0.0f, 0.0f);
		else if (editConstraint == 2)
			lockVec.NewVector(0.0f, 0.0f, 1.0f);

		if (viewType != VT_3D && editConstraint != 0)
			pLockVec = &lockVec;

		if (editConstraint)
			(m.Transform(lockVec)).SubVector(view->GetPosition());

		SnapVector2World(moveVec, pLockVec);

		view->CalInvTransSpaceMatrix(m.SetIdentity());
		m.Transform(moveVec);
	}

	moveVec.SubVector(tempSnapVec);
	tempSnapVec.AddVector(moveVec);

	if (modifyBrushMode == OPM_FACE_MOVE)
		pDoc->FaceMove(pManipFace, moveVec, *view, false);
	else if (modifyBrushMode == OPM_EDGE_MOVE)
		pDoc->EdgeMove(pManipEdge, moveVec, *view, false);
	else if (modifyBrushMode == OPM_VERTEX_MOVE)
		pDoc->VertexMove(manipVertex, moveVec, *view, false);

	// Update only self.
	OnUpdate(NULL, NULL);
}

void QView::OnLMBUModifyBrush(wxMouseEvent &event)
{
	QooleDoc *pDoc = GetDocument();

	// Need to turn off timer rendering for unfocused qviews.
	SetTimerUpdateQViews(false);

	// Turn off wireframe approximation rendering.
	View::ApproxItems(false);

	if (dragOperate)
	{
		Geometry *pOldBrush = pDoc->BrushManipulateEnd(false);

		if (pOldBrush != NULL)
		{
			Selector *pSlctr = &(GetMainFrame()->GetSelector());
			ASSERT(pSlctr->GetNumMSelectedObjects() == 1);
			Object *pManipObj = (pSlctr->GetMSelectedObjects())[0].GetPtr();

			OpBrushModification *op =
				new OpBrushModification(*pManipObj, *pOldBrush, qviewsBefore);
			GetMainFrame()->CommitOperation(*op);
		}
		else
		{
			//TODO: port.
			// MessageBeep(MB_OK);
			delete qviewsBefore;
		}

		dragOperate = false;
		qviewsBefore = NULL;
	}

	// Restore manip dot displays.
	pManipFace = NULL;
	pManipEdge = NULL;
	manipVertex = -1;
	dispManipDots = true;

	// Upate all QViews
	QDocHint hint;
	hint.flags = DUAV_QVIEWS | DUQV_UPDATEMANIPSTAT;
	pDoc->UpdateAllViews(NULL, &hint);

	// Update QTreeView.
	hint.flags = DUAV_NOQVIEWS | DUAV_OBJSMODATTRIB;
	hint.scope = view->GetViewObjPtr();
	pDoc->UpdateAllViews(NULL, &hint);

	// Update the status bar
	GetMainFrame()->UpdateStatusBar(_("Ready"));
}

// Plane Cut, Mirror / Flip
void QView::OnLMBDDefineOpPlane(wxMouseEvent &event)
{
	dragOperate = false;

	// Disable plane defs in 3d window.
	if (viewType == VT_3D)
		return;

	wxPoint point = event.GetPosition();
	if (planeDefined)
	{
		// See if down clicked on plane drag pts.
		ASSERT(dragPlanePt == 0);
		wxPoint pt;

		// Check 1st pt.
		LC2DC(planeDragPt1.GetX(), planeDragPt1.GetZ(), pt);
		pt -= point;
		if (ABS(pt.x) <= DRAG_CLICKPOS_EPSILON &&
			ABS(pt.y) <= DRAG_CLICKPOS_EPSILON)
		{
			dragOperate = true;
			dragPlanePt = 1;
		}

		// Check 1st pt.
		LC2DC(planeDragPt2.GetX(), planeDragPt2.GetZ(), pt);
		pt -= point;
		if (ABS(pt.x) <= DRAG_CLICKPOS_EPSILON &&
			ABS(pt.y) <= DRAG_CLICKPOS_EPSILON)
		{
			dragOperate = true;
			dragPlanePt = 2;
		}
	}
	else
	{
		dragOperate = true;
	}

	if (dragOperate) // Setup snapping.
	{
		Matrix44 m;
		SetSnapScope(*view->GetViewObjPtr());
		view->CalTransSpaceMatrix(tempSnapMatrix);
		view->CalInvTransSpaceMatrix(m.SetIdentity());
		m.Multiply(tempSnapInvMatrix);
		tempSnapInvMatrix = m;

		// Update the status bar
		GetMainFrame()->UpdateStatusBar(_("Press the Ctrl key to prevent snapping"));
	}

	if (planeDefined)
		return;

	planeDefined = true;

	// Turn off all plane defs in other qviews.
	QooleDoc *pDoc = GetDocument();
	QDocHint hint;
	hint.flags = DUAV_QVIEWS | DUQV_INITPLANEDRAG;
	pDoc->UpdateAllViews(this, &hint);

	// Define the first plane pt.
	float x, y;
	DC2LC(point, x, y);
	planeDragPt1.NewVector(x, 0.0f, y);

	// Snap the first plane pt.
	SnapVector2World(planeDragPt1);

	// Setup the dragging for the 2nd pt.
	dragPlanePt = 2;
	planeDragPt2 = planeDragPt1;

	// Update self to display the drag manip dots.
	OnUpdate(NULL, NULL);
}

void QView::OnLMBUDefineOpPlane(wxMouseEvent &event)
{
	if (!dragOperate)
		return;

	dragOperate = false;
	dragPlanePt = 0;

	// Update self to display the drag manip dots.
	OnUpdate(NULL, NULL);

	// TODO: Update the status bar
	/*CString mesg;
	mesg.LoadString(AFX_IDS_IDLEMESSAGE);
	pQMainFrame->UpdateStatusBar(mesg);*/
}

void QView::OnMMDefineOpPlane(wxMouseEvent &event)
{
	if (!dragOperate)
		return;

	wxPoint point = event.GetPosition();
	// Optimization.  Drop mouse move messages to speed up updates.
	// TODO: Port this.
	/*CPoint currPos;
	GetCursorPos(&currPos);
	ScreenToClient(&currPos);
	currPos -= point;
	if (ABS(currPos.x) > 1 || ABS(currPos.y) > 1) {
		static int count = 0;
		TRACE1("skip %d\n", count++);
		return;
	}*/

	// Clip the mouse coord in window.
	wxRect rect = GetViewFrame()->GetClientRect();
	if (!rect.Contains(point))
	{
		point.x = Max(rect.GetLeft(), Min(point.x, rect.GetRight()));
		point.y = Max(rect.GetTop(), Min(point.y, rect.GetBottom()));
	}

	// Define the new drag pt.
	ASSERT(dragPlanePt == 1 || dragPlanePt == 2);
	Vector3d *pDragPt = (dragPlanePt == 1 ? &planeDragPt1 : &planeDragPt2);

	float x, y;
	DC2LC(point, x, y);
	pDragPt->NewVector(x, 0.0f, y);

	bool snap = GetDocument()->GetGridSnap() && !event.ControlDown();

	if (snap)
	{
		SnapVector2World(*pDragPt);
	}

	// Update self to display the drag manip dots.
	OnUpdate(NULL, NULL);
}

void QView::OnRMBDDefineOpPlane(wxMouseEvent &event)
{
	// Turn off all plane defs in other qviews.
	QooleDoc *pDoc = GetDocument();
	QDocHint hint;
	hint.flags = DUAV_QVIEWS | DUQV_INITPLANEDRAG;
	pDoc->UpdateAllViews(this, &hint);
}

Plane QView::CalcOpPlane(void)
{
	ASSERT(planeDefined);

	Vector3d pt0(planeDragPt1);
	pt0.SetY(clipSide < 0 ? -100.0f : 100.0f);
	Plane opPlane(pt0, planeDragPt1, planeDragPt2);

	Matrix44 m;
	view->CalTransSpaceMatrix(m.SetIdentity());
	m.Transform(opPlane);
	return opPlane;
}

// Hide the mouse during drag operations
void QView::ReCenterMousePos(wxPoint &ptMousePos) {
	// TODO: Port this.
	// Adjust the mouse to the middle of screen if it's near edges.
	// Used during operations where the mouse is hidden.
/*
	CRect deskRect;
	// (GetDesktopWindow())->GetWindowRect(&deskRect);

	// Use the main window instead of desktopa
	pQMainFrame->GetWindowRect(&deskRect);

	CRect boundRect(deskRect);
	// boundRect.DeflateRect(deskRect.right / 4, deskRect.bottom / 4);
	boundRect.DeflateRect(deskRect.Width() / 5, deskRect.Height() / 5);

	CPoint ptTemp(ptMousePos);
	ClientToScreen(&ptTemp);
	if (boundRect.PtInRect(ptTemp)) {
		// Do nothing.  We're fine.
		return;
	}

	// The mouse cursor is outside the bounding area.
	// Center it.

	long cx = ptTemp.x = (deskRect.left + deskRect.right) / 2;
	long cy = ptTemp.y = (deskRect.top + deskRect.bottom) / 2;
	ScreenToClient(&ptTemp);

	// Traverse through message queue to modify mouse events.
	// ......

	// Adjust both last pos and current pos relative to center.
	lastMPos.x -= ptMousePos.x - ptTemp.x;
	lastMPos.y -= ptMousePos.y - ptTemp.y;
	ptMousePos = ptTemp;

	// Center cursor.
	::SetCursorPos(cx, cy);*/
}

// Timer update the other locked views
#define UPDATETIME_RENDERLOCKEDQVIEWS	100

void QView::SetTimerUpdateLockedQViews(bool set, wxMouseEvent &mevent)
{
	// Return if not a locked view.
	if (!GetMainFrame()->IsLockedView(this))
		return;

	// Return if updates of other qviews' not required.
	if (GetMainFrame()->GetOpMode() == OPM_EYE_ROTATE &&
		(mevent.RightIsDown() || viewType != VT_3D))
		return;

	if (GetMainFrame()->GetOpMode() == OPM_EYE_MOVE &&
		mevent.RightIsDown() && viewType == VT_3D)
		return;

	if (!set)
	{
		// Turn off timer.
		timerRenderLockedViews.Stop();
		// One last update.
		wxTimerEvent ev;
		OnTimerUpdateLockedQViews(ev);
		return;
	}

	// Set timer.
	timerRenderLockedViews.Start(UPDATETIME_RENDERLOCKEDQVIEWS, wxTIMER_CONTINUOUS);
}

void QView::OnTimerUpdateLockedQViews(wxTimerEvent &event)
{
	std::vector<QView*> locked;

	GetMainFrame()->GetLockedQViews(locked);
	for(size_t i = 0; i < locked.size(); i++)
	{
		if(locked[i] == this)
			continue;
		if(updateSclBoundBox)
			locked[i]->CalcScaleManipDots();
		locked[i]->OnUpdate(NULL, NULL);
	}

	// Hack to fix the scaleBoundBox update during
	//  eye move w/ middle mouse drag
	updateSclBoundBox = false;
}

// Button Down Click
void QView::OnMBDEyeMoveRotateZoom(wxMouseEvent &event)
{
	// Hide the mouse.
	wxSetCursor(wxNullCursor);

	wxPoint point = event.GetPosition();
	lastMPos = point;
	ReCenterMousePos(point);

	// Turn on timer rendering for locked qviews.
	SetTimerUpdateLockedQViews(true, event);

	// Turn on wireframe approximation rendering.
	View::ApproxItems(true);
}

void QView::OnMBUEyeMoveRotateZoom(wxMouseEvent &event)
{
	// Turn off timer rendering.
	SetTimerUpdateLockedQViews(false, event);

	// Turn off wireframe approximation rendering.
	View::ApproxItems(false);

	// Restore the cursor.
	wxSetCursor(*wxSTANDARD_CURSOR);

	// Refresh all views.
	QooleDoc *pDoc = GetDocument();
	QDocHint hint;
	hint.flags = DUAV_SUPDATEALL;
	pDoc->UpdateAllViews(NULL, &hint);
}

void QView::OnLMBEyeMove(wxMouseEvent &event)
{
	wxPoint point = event.GetPosition();
	if (!mouseMDrag && event.ShiftDown())
	{
		// Rotate the view.
		OnLMBEyeRotate(event);
		return;
	}

	// Check for cursor adjustments.
	ReCenterMousePos(point);

	wxPoint diffVec(point);
	diffVec -= lastMPos;

	float x, y;
	DU2LU(diffVec, x, y);
	Vector3d moveVec(x, 0.0f, y);

	// Move it.
	MoveEyeView(moveVec);

	lastMPos = point;
}

void QView::MoveEyeView(const Vector3d &moveVec)
{
	Vector3d mVec(moveVec);

	OperationMode opMode = GetMainFrame()->GetOpMode();
	if (GetMainFrame()->IsLockedView(this)) // Move affects other views.
	{
		Matrix44 trans;
		trans.SetRotate(view->GetOrientation());
		trans.Transform(mVec);

		Vector3d editPos = GetMainFrame()->GetEditFocusPos();
		editPos.AddVector(mVec);
		GetMainFrame()->SetEditFocusPos(editPos, false);

		if (opMode == OPM_OBJECT_SCALE || opMode == OPM_OBJECT_MULTITOOL)
			updateSclBoundBox = true;
		if (planeDefined)
		{
			planeDragPt1.SubVector(moveVec);
			planeDragPt2.SubVector(moveVec);
		}
	}
	else // Just move self.
	{
		view->MoveRelPosition(mVec);
	}

	if (opMode == OPM_OBJECT_SCALE || opMode == OPM_OBJECT_MULTITOOL)
	{
		// Update the scale box.
		for(int i = 0; i < 8; i++)
			scaleManipDots[i].SubVector(moveVec);
	}

	// Just update self.
	OnUpdate(NULL, NULL);
}

void QView::OnEndViewMove(wxTimerEvent &event)
{
	OnTimerUpdateLockedQViews(event);
	timerUpdateViews.Stop();
}

// Zoom stuffs
#define ZOOM_SPEED  0.01f
#define ZOOM_MIN	0.025f
#define ZOOM_MAX	10.0f

// Zoom the view for 2d wins.
// Change orbit radius for locked 3d view.
// Move view forward/backward for unlocked 3d view.
void QView::OnRMBEyeZoom(wxMouseEvent &event)
{
	OperationMode opMode = GetMainFrame()->GetOpMode();

	if (opMode != OPM_EYE_ZOOM && !mouseMDrag && event.ShiftDown())
	{
		// Use the rotate function.
		OnRMBEyeRotate(event);
		return;
	}

	// Check for cursor adjustments.
	wxPoint point = event.GetPosition();
	ReCenterMousePos(point);

	wxPoint diffVec(point);
	diffVec -= lastMPos;

	if (viewType != VT_3D)
	{
		// It's a 2d window.
		float newZoomVal = zoomVal * (1.0f - ZOOM_SPEED * diffVec.y);

		if (newZoomVal < ZOOM_MIN || newZoomVal > ZOOM_MAX)
			newZoomVal = zoomVal;

		if (GetMainFrame()->IsLockedView(this))
		{
			// zoom affects other views.
			GetMainFrame()->Set2DLockedZoom(newZoomVal, false);
		}
		else
		{
			zoomVal = newZoomVal;
			OnZoomChanged();
		}
	}
	else
	{
		// 3d window.
		float x, y;
		DU2LU(diffVec, x, y);
		Vector3d moveVec;

		if (GetMainFrame()->IsLockedView(this))
		{
			// Assume 3d view will always face edit focus pos.
			Vector3d radiusVec = view->GetPosition();
			radiusVec.SubVector(GetMainFrame()->GetEditFocusPos());
			float r = radiusVec.GetMag();
			y = Min(y, r - 1.1f);
			moveVec.NewVector(0.0f, y, 0.0f);
		}
		else
		{
			moveVec.NewVector(x, y, 0.0f);
		}

		view->MoveRelPosition(moveVec);
	}

	// Update just self.
	OnUpdate(NULL, NULL);
	lastMPos = point;
}

void QView::ZoomEyeView(float depthVal)
{
	if (viewType != VT_3D)
	{
		// It's a 2d window.
		float newZoomVal = zoomVal * (1.0f + ZOOM_SPEED * depthVal);

		if (newZoomVal < ZOOM_MIN || newZoomVal > ZOOM_MAX)
			newZoomVal = zoomVal;

		if (GetMainFrame()->IsLockedView(this))
		{
			// zoom affects other views.
			GetMainFrame()->Set2DLockedZoom(newZoomVal, false);
		}
		else
		{
			zoomVal = newZoomVal;
			OnZoomChanged();
		}
	}
	else
	{
		// 3d window.
		Vector3d moveVec;

		if (GetMainFrame()->IsLockedView(this))
		{
			// Assume 3d view will always face edit focus pos.
			Vector3d radiusVec = view->GetPosition();
			radiusVec.SubVector(GetMainFrame()->GetEditFocusPos());
			float r = radiusVec.GetMag();
			depthVal = Min(depthVal, r - 1.1f);
			moveVec.NewVector(0.0f, depthVal, 0.0f);
		}
		else
		{
			moveVec.NewVector(0.0f, depthVal, 0.0f);
		}

		view->MoveRelPosition(moveVec);
	}

	// Update just self.
	OnUpdate(NULL, NULL);
}

// Use the mouse wheel to change the zoom.
#define UPDATETIME_ENDZOOM		150

void QView::OnMouseWheel(wxMouseEvent &event)
{
	int delta = event.GetWheelRotation();
	int n = ABS(delta / 120);
	for(;n > 0; n--)
	{
		ZoomEyeView(delta > 0 ? 32.0f : -32.0f);
	}

	// Set a timer to update all other views.
	if (GetMainFrame()->IsLockedView(this))  // Move affects other views.
		timerUpdateViews.Start(UPDATETIME_ENDZOOM, wxTIMER_CONTINUOUS);
}

void QView::OnViewZoomIn(void)
{
	if (viewType == VT_3D)
		return;

	ZoomEyeView(32.0f);

	if (GetMainFrame()->IsLockedView(this))
	{
		// Move affects other views.
		QooleDoc *pDoc = GetDocument();
		QDocHint hint;
		hint.flags = DUAV_SUPDATEALL | DUAV_QVIEWS;
		pDoc->UpdateAllViews(this, &hint);
	}
}

void QView::OnViewZoomOut(void)
{
	if (viewType == VT_3D)
		return;

	ZoomEyeView(-32.0f);

	if (GetMainFrame()->IsLockedView(this))
	{
		// Move affects other views.
		QooleDoc *pDoc = GetDocument();
		QDocHint hint;
		hint.flags = DUAV_SUPDATEALL | DUAV_QVIEWS;
		pDoc->UpdateAllViews(this, &hint);
	}
}

// Eye rotate.

// Rotate view orientation
void QView::OnLMBEyeRotate(wxMouseEvent &event)
{
	if (event.ControlDown())
	{
		// Move the view.
		OnLMBEyeMove(event);
		return;
	}

	// Only applies to 3d views.
	if (viewType != VT_3D)
		return;

	// Readjust cursor.
	wxPoint point = event.GetPosition();
	ReCenterMousePos(point);

	float radius;
	bool viewLocked = GetMainFrame()->IsLockedView(this);
	if (viewLocked)
	{
		// Need to remember old radius.
		Vector3d radiusVec = view->GetPosition();
		radiusVec.SubVector(GetMainFrame()->GetEditFocusPos());
		radius = radiusVec.GetMag();
		ASSERT(radius >= 1.0f);  // Sanity.
	}

	// Get rotation vector.
	wxRect rect = GetViewFrame()->GetClientRect();

	wxPoint diffVec(point);
	diffVec -= lastMPos;

	float x, y;
	x = diffVec.x * 180.0f / rect.GetRight();
	y = diffVec.y * 180.0f / rect.GetBottom();
	x *= sensitivity / 5.0f;
	y *= sensitivity / 5.0f;

	if (!invLMouse)
		y *= -1.0f;

	// Rotate the view's orientation.
	SphrVector orientVec;
	float angYaw, angPitch;

	view->GetOrientation(orientVec);
	angPitch = orientVec.GetPitch() + DEG2RAD(y);
	angPitch = Min(Max(angPitch, DEG2RAD(-89.9f)), DEG2RAD(89.9f));
	angYaw = orientVec.GetYaw() + DEG2RAD(x);
	orientVec.NewVector(angYaw, angPitch, 0.0f);
	view->SetOrientation(orientVec);

	if (viewLocked)
	{
		// Calculate new focus pos
		Vector3d newFocusPos(0.0f, radius, 0.0f);
		Matrix44 trans;
		trans.SetRotate(orientVec);
		trans.Transform(newFocusPos);
		newFocusPos.AddVector(view->GetPosition());
		GetMainFrame()->SetEditFocusPos(newFocusPos, false);
	}

	// Update only self.
	OnUpdate(NULL, NULL);
	lastMPos = point;
}

void QView::OnRMBEyeRotate(wxMouseEvent &event)
{
	if (event.ControlDown())
	{
		// Move view.
		OnRMBEyeZoom(event);
		return;
	}

	// Only applies to 3d locked views.
	if (viewType != VT_3D || !GetMainFrame()->IsLockedView(this))
		return;

	// Readjust cursor.
	wxPoint point = event.GetPosition();
	ReCenterMousePos(point);

	// Get rotation vector.
	wxRect rect = GetViewFrame()->GetClientRect();

	wxPoint diffVec(point);
	diffVec -= lastMPos;

	float x, y;
	x = diffVec.x * 180.0f / rect.GetRight();
	y = diffVec.y * 180.0f / rect.GetBottom();
	x *= sensitivity / 5.0f;
	y *= sensitivity / 5.0f;

	if (invRMouse)
	{
		x *= -1.0f;
		y *= -1.0f;
	}

	// Get radius distance.
	Vector3d radiusVec;
	radiusVec.SubVector(view->GetPosition(), GetMainFrame()->GetEditFocusPos());
	ASSERT(radiusVec.GetMag() >= 1.0f); // Sanity.

	// Get rotation vector.
	SphrVector orientVec;
	float angYaw, angPitch;
	orientVec.NewVector(radiusVec, Vector3d::zAxisVec);
	angYaw = orientVec.GetYaw() + DEG2RAD(x);
	angPitch = orientVec.GetPitch() + DEG2RAD(y);
	angPitch = Min(Max(angPitch, DEG2RAD(-89.5f)), DEG2RAD(89.5f));
	orientVec.NewVector(angYaw, angPitch, 0.0f);

	// Rotate it.
	Matrix44 trans;
	trans.SetRotate(orientVec);
	radiusVec.NewVector(0.0f, radiusVec.GetMag(), 0.0f);
	trans.Transform(radiusVec);

	// Set view pos.
	radiusVec.AddVector(GetMainFrame()->GetEditFocusPos());
	view->SetPosition(radiusVec);

	// Set view orientataion.
    orientVec.NewVector(orientVec.GetYaw() + DEG2RAD(180.0f),
						-orientVec.GetPitch(), 0.0f);
	view->SetOrientation(orientVec);

	// update view.
	OnUpdate(NULL, NULL);

	lastMPos = point;
}

// Fly Through
void QView::OnStartFlyThrough(void)
{
	QDraw::OutputText(_("Switching to flythrough mode... "));
	ASSERT(GetMainFrame()->GetOpMode() == OPM_FLY_THROUGH);
	ASSERT(viewType == VT_3D);

	// Update status bar.
	GetMainFrame()->UpdateStatusBar();

	// Capture mouse.
	wxWindow *frame = GetViewFrame();
	frame->CaptureMouse();

	// Hide the mouse.
	wxSetCursor(wxNullCursor);

	// Remember mouse pos.
	ptClick = wxGetMousePosition();
	lastMPos = ptClick;

	//TODO: ScreenToClient(&lastMPos);

	QDraw::OutputText(_("OK.\n"));

	// Init mouse pos.
	wxPoint point(lastMPos);
	ReCenterMousePos(point);

	// Remember old view pos for later.
	if (GetMainFrame()->IsLockedView(this))
	{
		downClickVec = GetMainFrame()->GetEditFocusPos();
		downClickVec.SubVector(view->GetPosition());
	}

	// Turn on wireframe approximation rendering.
	//View::ApproxItems(true);

	flyTime = GetTime();

	// Empty the queue of mouse and key events.
	wxWindowDisabler disable(GetViewFrame());
	wxEventLoop *loop = wxEventLoop::GetActive();
	while(loop->Pending())
		loop->Dispatch();

	// Busy loop for input.
	while (true) {
		// While idling, check key state.
		while(!loop->Pending())
			OnKeyCheckFlyThrough();

		// Check for mode exit.
		if (wxGetMouseState().LeftIsDown() || wxGetKeyState(WXK_ESCAPE))
		{
			QDraw::OutputText(_("Switching back to editing mode... "));
			break;
		}

		// To prevent delayed input processing,
		//  check and clear out all mouse move events on the queue.
		while(loop->Pending())
			loop->Dispatch();

		OnKeyCheckFlyThrough();
	}

	// Turn off wireframe approximation rendering.
	View::ApproxItems(false);

	// Restore original mouse pos & cursor.
	//::SetCursorPos(ptClick.x, ptClick.y);
	wxSetCursor(*wxSTANDARD_CURSOR);

	// Release the mouse.
	frame->ReleaseMouse();

	// Calc new focus pos.
	if (GetMainFrame()->IsLockedView(this))
	{
		Vector3d diffVec(0.0f, downClickVec.GetMag(), 0.0f);
		Matrix44 m;
		m.SetRotate(view->GetOrientation());
		m.Transform(diffVec);
		diffVec.AddVector(view->GetPosition());
		GetMainFrame()->Set3DViewLock(false);
		GetMainFrame()->SetEditFocusPos(diffVec);
		GetMainFrame()->Set3DViewLock(true);
	}
	QDraw::OutputText(_(" OK.\n"));
}

#define GETKEYSTATE(x) wxGetKeyState(wxKeyCode(x))
void QView::OnKeyCheckFlyThrough(bool forceUpdate)
{
	ASSERT(GetMainFrame()->GetOpMode() == OPM_FLY_THROUGH);
	ASSERT(viewType == VT_3D);
	ASSERT(GetViewFrame()->HasCapture());

	if (viewType != VT_3D)
		return;

	Vector3d moveVec;

	// Determine distance to move, based on time since last frame
	// This preserves a constant movement speed in the world
	float moveDist = walkSpeed * (float)(GetTime() - flyTime) / 50.0f;
	moveDist = Min(moveDist, walkSpeed);
	flyTime = GetTime();

	if (GETKEYSTATE(keyForward))
		moveVec.SetY(moveVec.GetY() + moveDist);
	if (GETKEYSTATE(keyBackward))
		moveVec.SetY(moveVec.GetY() - moveDist);
	if (GETKEYSTATE(keyLeft))
		moveVec.SetX(moveVec.GetX() - moveDist);
	if (GETKEYSTATE(keyRight))
		moveVec.SetX(moveVec.GetX() + moveDist);
	if (GETKEYSTATE(keyUp))
		moveVec.SetZ(moveVec.GetZ() + moveDist);
	if (GETKEYSTATE(keyDown))
		moveVec.SetZ(moveVec.GetZ() - moveDist);

	view->MoveRelPosition(moveVec);

	// Only update when neccessary.
	if (!(moveVec == Vector3d::origVec) || forceUpdate)
	{
		OnUpdate(NULL, NULL);
	}
}

void QView::OnMMFlyThrough(wxMouseEvent &event)
{
	ASSERT(GetMainFrame()->GetOpMode() == OPM_FLY_THROUGH);
	ASSERT(viewType == VT_3D);

	if (viewType != VT_3D)
		return;

	// Check for cursor adjustments.
	wxPoint point = event.GetPosition();
	ReCenterMousePos(point);

	// Get rotation vector.
	wxRect rect = GetViewFrame()->GetClientRect();

	wxPoint diffVec(point);
	diffVec -= lastMPos;

	float x, y;
	x = diffVec.x * 180.0f / rect.GetRight();
	y = diffVec.y * 180.0f / rect.GetBottom();
	// x *= 2.0f;
	// y *= 2.0f;

	if (!invLMouse)
		y *= -1.0f;

	// Rotate the view's orientation.
	SphrVector orientVec;
	float angYaw, angPitch;

	view->GetOrientation(orientVec);
	angPitch = orientVec.GetPitch() + DEG2RAD(y);
	angPitch = Min(Max(angPitch, DEG2RAD(-89.9f)), DEG2RAD(89.9f));
	angYaw = orientVec.GetYaw() + DEG2RAD(x);
	orientVec.NewVector(angYaw, angPitch, 0.0f);
	view->SetOrientation(orientVec);

	lastMPos = point;

	// Let the key check handler do the render update.
	OnKeyCheckFlyThrough(true);
}

// Event handlers.
void QView::OnLButtonDown(wxMouseEvent &event)
{
	if(GetMainFrame()->GetOpMode() == OPM_FLY_THROUGH)
		return;

	// Activate the view.
	Activate(true);

	// Disable left click during right button ops.
	if (mouseMClick || mouseMDrag || mouseRClick || mouseRDrag)
		return;

	GetViewFrame()->CaptureMouse();
	mouseLDrag = false;
	mouseLClick = true;
	ptClick = event.GetPosition();

	float x, y;
	DC2LC(event.GetPosition(), x, y);
	downClickVec.NewVector(x, y, 0.0f);

	switch(GetMainFrame()->GetOpMode())
	{
	case OPM_OBJECT_MULTITOOL:
		OnLMBDObjectsMultiTool(event);
		break;
	case OPM_OBJECT_SELECT:
		OnLMBDObjectsSelect(event);
		break;
	case OPM_OBJECT_MOVE:
	case OPM_OBJECT_ROTATE:
	case OPM_OBJECT_SCALE:
		OnLMBDObjectsMoveRotateScale(event);
		break;
	case OPM_MODIFY_BRUSH:
	case OPM_FACE_MOVE:
	case OPM_EDGE_MOVE:
	case OPM_VERTEX_MOVE:
		OnLMBDModifyBrush(event);
		break;
	case OPM_MIRROR:
	case OPM_PLANE_CLIP:
		OnLMBDDefineOpPlane(event);
		break;
	case OPM_EYE_MOVE:
	case OPM_EYE_ROTATE:
	case OPM_EYE_ZOOM:
		OnMBDEyeMoveRotateZoom(event);
		break;
	default:
		// Do nothing
		break;
	}

	paintCount = 0;
}

void QView::OnLButtonUp(wxMouseEvent &event)
{
	if(GetMainFrame()->GetOpMode() == OPM_FLY_THROUGH)
		return;

	if (!mouseLClick && !mouseLDrag)
		return;

	switch(GetMainFrame()->GetOpMode())
	{
	case OPM_OBJECT_MULTITOOL:
		OnLMBUObjectsMultiTool(event);
		break;
	case OPM_OBJECT_SELECT:
		OnLMBUObjectsSelect(event);
		break;
	case OPM_OBJECT_MOVE:
		OnLMBUObjectsMove(event);
		break;
	case OPM_OBJECT_ROTATE:
		OnLMBUObjectsRotate(event);
		break;
	case OPM_OBJECT_SCALE:
		OnLMBUObjectsScale(event);
		break;
	case OPM_MODIFY_BRUSH:
	case OPM_FACE_MOVE:
	case OPM_EDGE_MOVE:
	case OPM_VERTEX_MOVE:
		OnLMBUModifyBrush(event);
		break;
	case OPM_MIRROR:
	case OPM_PLANE_CLIP:
		OnLMBUDefineOpPlane(event);
		break;
	case OPM_EYE_MOVE:
	case OPM_EYE_ROTATE:
	case OPM_EYE_ZOOM:
		OnMBUEyeMoveRotateZoom(event);
		break;
	default:
		// Do nothing
		break;
	}

	mouseLDrag = false;
	mouseLClick = false;
	GetViewFrame()->ReleaseMouse();
}

void QView::OnMButtonDown(wxMouseEvent &event)
{
	// Skip in fly through mode.
	if (GetMainFrame()->GetOpMode() == OPM_FLY_THROUGH)
		return;

	// Activate the view.
	Activate(true);

	// Disable middle click during other operations.
	if (mouseLClick || mouseLDrag || mouseRClick || mouseRDrag)
		return;

	// Hack around a windows bug.
	// Sometimes Windows 95/98 think middle button is down
	//  even when it's released.  Prevents this func
	//  being called 2x and losing the cursor display
	//  during/after panning.
	if (mouseMClick || mouseMDrag)
		return;

	GetViewFrame()->CaptureMouse();
	mouseMDrag = false;
	mouseMClick = true;
	ptClick = event.GetPosition();

	float x, y;
	DC2LC(event.GetPosition(), x, y);
	downClickVec.NewVector(x, y, 0.0f);

	// Pan the view.
	OnMBDEyeMoveRotateZoom(event);
}

void QView::OnMButtonUp(wxMouseEvent &event)
{
	// Skip in fly through.
	if (GetMainFrame()->GetOpMode() == OPM_FLY_THROUGH)
		return;

	if (!mouseMClick && !mouseMDrag)
		return;

	float x, y;
	DC2LC(event.GetPosition(), x, y);
	upClickVec.NewVector(x, y, 0.0f);

	// End pan view.
	OnMBUEyeMoveRotateZoom(event);

	mouseMDrag = false;
	mouseMClick = false;
	GetViewFrame()->ReleaseMouse();
}

void QView::OnRButtonDown(wxMouseEvent &event)
{
	if (GetMainFrame()->GetOpMode() == OPM_FLY_THROUGH)
		return;

	// Activate the view.
	Activate(true);

	// Disable right button during left button action.
	if (mouseLClick || mouseLDrag || mouseMClick || mouseMDrag)
		return;

	viewFrame->CaptureMouse();
	mouseRDrag = false;
	mouseRClick = true;
	ptClick = event.GetPosition();

	float x, y;
	DC2LC(event.GetPosition(), x, y);
	downClickVec.NewVector(x, y, 0.0f);

	switch (GetMainFrame()->GetOpMode())
	{
	case OPM_EYE_MOVE:
	case OPM_EYE_ROTATE:
	case OPM_EYE_ZOOM:
		OnMBDEyeMoveRotateZoom(event);
		break;
	case OPM_PLANE_CLIP:
	case OPM_MIRROR:
		OnRMBDDefineOpPlane(event);
	default:
		// Do nothing.
		break;
	}
}

void QView::OnRButtonUp(wxMouseEvent &event)
{
	if (GetMainFrame()->GetOpMode() == OPM_FLY_THROUGH)
		return;

	if (!mouseRClick && !mouseRDrag)
		return;

	// Release mouse capture.
	viewFrame->ReleaseMouse();
	
	float x, y;
	DC2LC(event.GetPosition(), x, y);
	upClickVec.NewVector(x, y, 0.0f);

	switch (GetMainFrame()->GetOpMode())
	{
	case OPM_EYE_MOVE:
	case OPM_EYE_ROTATE:
	case OPM_EYE_ZOOM:
		OnMBUEyeMoveRotateZoom(event);
		break;
	case OPM_OBJECT_MULTITOOL:
	case OPM_OBJECT_SELECT:
	case OPM_OBJECT_ROTATE:
	case OPM_OBJECT_MOVE:
	case OPM_OBJECT_SCALE:
	case OPM_MODIFY_BRUSH:
	case OPM_FACE_MOVE:
	case OPM_EDGE_MOVE:
	case OPM_VERTEX_MOVE:
	case OPM_PLANE_CLIP:
	case OPM_MIRROR:
		OnRMBUContextMenu(event);
		break;
	default:
		// Do nothing.
		break;
	}

	mouseRDrag = false;
	mouseRClick = false;
}

void QView::OnMouseMove(wxMouseEvent &event)
{
	if(GetMainFrame()->GetOpMode() == OPM_FLY_THROUGH)
	{
		if(viewType == VT_3D)
			OnMMFlyThrough(event);
		return;
	}

	UpdateCoordInfo(event.GetPosition());

	if (!event.LeftIsDown() && !event.MiddleIsDown() && !event.RightIsDown())
		return;

	if (mouseLClick || mouseMClick || mouseRClick)
	{
		wxPoint delta = ptClick - event.GetPosition();
		if (abs(delta.x) > dragThreshold ||
			abs(delta.y) > dragThreshold)
		{
			if (mouseLClick)
				mouseLDrag = true;
			else if (mouseMClick)
				mouseMDrag = true;
			else if (mouseRClick)
				mouseRDrag = true;
			mouseLClick = mouseMClick = mouseRClick = false;
		}
	}

	if (!mouseLDrag && !mouseMDrag && !mouseRDrag)
		return;

	// panning.
	if (mouseMDrag)
	{
		OnLMBEyeMove(event);
		return;
	}

	switch(GetMainFrame()->GetOpMode())
	{
	case OPM_OBJECT_MULTITOOL:
		OnMMObjectsMultiTool(event);
		break;
	case OPM_OBJECT_SELECT:
		OnMMObjectsSelect(event);
		break;
	case OPM_OBJECT_MOVE:
		OnMMObjectsMove(event);
		break;
	case OPM_OBJECT_ROTATE:
		OnMMObjectsRotate(event);
		break;
	case OPM_OBJECT_SCALE:
		OnMMObjectsScale(event);
		break;
	case OPM_MODIFY_BRUSH:
	case OPM_FACE_MOVE:
	case OPM_EDGE_MOVE:
	case OPM_VERTEX_MOVE:
		OnMMModifyBrush(event);
		break;
	case OPM_MIRROR:
	case OPM_PLANE_CLIP:
		OnMMDefineOpPlane(event);
		break;
	case OPM_EYE_MOVE:
		if (mouseLDrag)
			OnLMBEyeMove(event);
		else if (mouseRDrag)
			OnRMBEyeZoom(event);
		break;
	case OPM_EYE_ROTATE:
		if (mouseLDrag)
			OnLMBEyeRotate(event);
		else if (mouseRDrag)
			OnRMBEyeRotate(event);
		break;
	case OPM_EYE_ZOOM:
		if (mouseLDrag || mouseRDrag)
			OnRMBEyeZoom(event);
		break;
	default:
		// Do nothing
		break;
	}
}

// Context menu.
void QView::OnContextMenu(wxContextMenuEvent &event)
{
	OperationMode opMode = GetMainFrame()->GetOpMode();
	if(opMode == OPM_OBJECT_MULTITOOL || opMode == OPM_OBJECT_SELECT ||
		opMode == OPM_OBJECT_MOVE || opMode == OPM_OBJECT_ROTATE ||
		opMode == OPM_OBJECT_SCALE)
	{
		wxMenu *addMenu = &GetMainFrame()->GetAddObjectMenu();

		wxMenuItem *sep = addMenu->AppendSeparator();
		wxMenuItem *item =addMenu->Append(XRCID("menuSavePrefab"), _("Save Prefab..."));
		wxMenuItem *sep2 = addMenu->AppendSeparator();
		wxMenuItem *item2 = addMenu->Append(XRCID("menuProperties"), _("Properties..."));

		// Popup the menu.
		viewFrame->PopupMenu(addMenu, event.GetPosition());

		addMenu->Delete(sep);
		addMenu->Delete(item);
		addMenu->Delete(sep2);
		addMenu->Delete(item2);
	}
}

void QView::OnRMBUContextMenu(wxMouseEvent &event)
{
	if (!mouseRClick && !mouseRDrag)
		return;

	// Check if point is still inside window.
	wxRect rect = GetViewFrame()->GetClientRect();
	if (!rect.Contains(event.GetPosition()))
		return;

	// Generate a context menu message.
	wxContextMenuEvent cev;
	cev.SetPosition(event.GetPosition());
	OnContextMenu(cev);
}

// Key events.
void QView::OnKeyUp(wxKeyEvent &event)
{
	// Handle the ctrl-add selection.
	if (event.GetKeyCode() != WXK_CONTROL || !selectAddPending)
		return;

	selectAddPending = false;

	Selector *pSlct = &(GetMainFrame()->GetSelector());
	Object *pSlctObj = pSlct->GetSSelectedObject();

	if (pSlctObj != NULL)
	{
		// Clear single selection.
		pSlct->SUnselect();

		// Add to selection.
		selectObjectsBuffer.DeleteAllNodes();
		selectObjectsBuffer.AppendNode(*(new ObjectPtr(pSlctObj)));

		// Commit operation
		OpSelectAddObjs *op = new OpSelectAddObjs(selectObjectsBuffer);
		GetMainFrame()->CommitOperation(*op);
	}
}

void QView::OnKeyDown(wxKeyEvent &event)
{

}

void QView::OnChar(wxKeyEvent &event)
{
}

// Edit paste
void QView::OnEditPaste(wxCommandEvent &event)
{
	Selector *pSlctr = &GetMainFrame()->GetSelector();
	ASSERT(pSlctr != NULL);

	Vector3d pasteVec;
	if (GetMainFrame()->IsLockedView(this))
	{
		pasteVec = GetMainFrame()->GetEditFocusPos();
	}
	else if (viewType != VT_3D) {
		pasteVec = view->GetPosition();
	}
	else
	{
		pasteVec.NewVector(0.0f, 128.0f, 0.0f);
		Matrix44 m;
		view->CalTransSpaceMatrix(m.SetIdentity());
		m.Transform(pasteVec);
	}

	SnapAddObjPos(pasteVec);

	OpEditPaste *opPaste = new OpEditPaste(pasteVec);
	GetMainFrame()->CommitOperation(*opPaste);
}

void QView::OnEditPasteUI(wxUpdateUIEvent &event)
{
	QooleDoc *pDoc = GetDocument();

	event.Enable(pDoc != NULL && (GetMainFrame()->clipBoard).NumOfElm() > 0);
}

// Edit duplicate.
void QView::OnEditDuplicate(wxCommandEvent &event)
{
	Vector3d mVec;

	if (viewType == VT_TOP)
		mVec.NewVector(16.0f, -16.0f, 0.0f);
	else if (viewType == VT_BACK)
		mVec.NewVector(16.0f, 0.0f, -16.0f);
	else if (viewType == VT_SIDE)
		mVec.NewVector(0.0f, 16.f, -16.0f);
	else if (viewType == VT_3D)
		mVec.NewVector(16.0f, -16.0f, -16.0f);

	OpEditDuplicate *opDup = new OpEditDuplicate(mVec);
	GetMainFrame()->CommitOperation(*opDup);
}

void QView::OnEditDuplicateUI(wxUpdateUIEvent &event)
{
	QooleDoc *pDoc = GetDocument();
	Selector *pSlctr = &GetMainFrame()->GetSelector();

	event.Enable(pDoc != NULL && pSlctr != NULL &&
				   pSlctr->GetNumMSelectedObjects() != 0);
}

// Edit - Increase/Decrease grid size.
void QView::OnEditIncrGridSize(wxCommandEvent &event)
{
	if (!QDraw::drawGridStep1)
		return;

	QDraw::OutputText(_("Increasing grid size... "));
	int base = GetDocument()->GetGame()->GetGridSizeBase();
	double newSize = log(QDraw::gridStep1) / log(base) + 1.0f;
	newSize = ROUND(newSize);
	newSize = Max(0.0f, Min(7.0f, newSize));
	newSize = pow(base, newSize);

	// Set the display grid size.
	QDraw::gridStep1 = (int) newSize;

	// Set the snap size.
	GetDocument()->SetGridSnapSize(newSize);

	// Update QViews.
	UpdateGridsDisplay();
	QDraw::OutputText(_("OK.\n"));
}

void QView::OnEditDecrGridSize(wxCommandEvent &event)
{
	if (!QDraw::drawGridStep1)
		return;

	QDraw::OutputText(_("Decreasing grid size... "));
	int base = GetDocument()->GetGame()->GetGridSizeBase();
	double newSize = log(QDraw::gridStep1) / log(base) - 1.0f;
	newSize = ROUND(newSize);
	newSize = Max(0.0f, Min(7.0f, newSize));
	newSize = (float) pow(base, newSize);

	// Set the display grid size.
	QDraw::gridStep1 = (int) newSize;

	// Set the snap size.
	GetDocument()->SetGridSnapSize((int) newSize);

	// Update QViews.
	UpdateGridsDisplay();
	QDraw::OutputText(_("OK.\n"));
}

void QView::UpdateGridsDisplay()
{
	QooleDoc *doc = GetMainFrame()->GetDeskTopDocument();
	ASSERT(doc);

	QDocHint hint;
	hint.flags = DUAV_QVIEWS | DUQV_UPDATEGRIDS;
	doc->UpdateAllViews(NULL, &hint);
}

// Render style.
void QView::OnRenderWireFrame(wxCommandEvent &event)
{
	SetRenderType(RT_WIREFRAME);
}

void QView::OnRenderWireFrameUI(wxUpdateUIEvent &event)
{
	std::vector<QView*> views;
	GetMainFrame()->Get3DViews(views);
	if(views.size() > 0)
		event.Check(views[0]->renderType == RT_WIREFRAME);
	else
		event.Check(false);
}

void QView::OnRenderSolid(wxCommandEvent &event)
{
	SetRenderType(RT_SOLID);
}

void QView::OnRenderSolidUI(wxUpdateUIEvent &event)
{
	std::vector<QView*> views;
	GetMainFrame()->Get3DViews(views);
	if(views.size() > 0)
		event.Check(views[0]->renderType == RT_SOLID);
	else
		event.Check(false);
}

void QView::OnRenderTextured(wxCommandEvent &event)
{
	SetRenderType(RT_TEXTURED);
}

void QView::OnRenderTexturedUI(wxUpdateUIEvent &event)
{
	std::vector<QView*> views;
	GetMainFrame()->Get3DViews(views);
	if(views.size() > 0)
		event.Check(views[0]->renderType == RT_TEXTURED);
	else
		event.Check(false);
}

void QView::OnRenderShaded(wxCommandEvent &event)
{
	SetRenderType(RT_SHADED);
}

void QView::OnRenderShadedUI(wxUpdateUIEvent &event)
{
	std::vector<QView*> views;
	GetMainFrame()->Get3DViews(views);
	if(views.size() > 0)
		event.Check(views[0]->renderType == RT_SHADED);
	else
		event.Check(false);
}

void QView::SetRenderType(RenderType rt)
{
	std::vector<QView*> views;
	GetMainFrame()->Get3DViews(views);
	for(size_t i = 0; i < views.size(); i++)
	{
		QView *view = views[i];

		view->renderType = rt;
		view->CreateQDraw();

		view->draw->RenderMode(rt);
		view->OnUpdate(NULL, NULL);
	}
}

// Changes the render library.
void QView::RefreshAll()
{
	std::vector<QView*> views;
	GetMainFrame()->GetViews(views);
	for(size_t i = 0; i < views.size(); ++i)
	{
		QView *view = views[i];
		view->CreateQDraw();

		view->draw->RenderMode(view->renderType);
		view->draw->Size(view->width, view->height);
		view->OnUpdate(NULL, NULL);
	}

}

